aboutsummaryrefslogtreecommitdiff
path: root/src/test
diff options
context:
space:
mode:
Diffstat (limited to 'src/test')
-rw-r--r--src/test/argsman_tests.cpp18
-rw-r--r--src/test/arith_uint256_tests.cpp11
-rw-r--r--src/test/blockchain_tests.cpp41
-rw-r--r--src/test/blockfilter_tests.cpp9
-rw-r--r--src/test/blockmanager_tests.cpp5
-rw-r--r--src/test/bloom_tests.cpp66
-rw-r--r--src/test/checkqueue_tests.cpp2
-rw-r--r--src/test/cluster_linearize_tests.cpp138
-rw-r--r--src/test/coins_tests.cpp37
-rw-r--r--src/test/coinscachepair_tests.cpp219
-rw-r--r--src/test/crypto_tests.cpp10
-rw-r--r--src/test/cuckoocache_tests.cpp12
-rw-r--r--src/test/descriptor_tests.cpp70
-rw-r--r--src/test/fuzz/addrman.cpp2
-rw-r--r--src/test/fuzz/bech32.cpp5
-rw-r--r--src/test/fuzz/bip324.cpp10
-rw-r--r--src/test/fuzz/bitset.cpp16
-rw-r--r--src/test/fuzz/block_header.cpp2
-rw-r--r--src/test/fuzz/block_index.cpp133
-rw-r--r--src/test/fuzz/cluster_linearize.cpp957
-rw-r--r--src/test/fuzz/coins_view.cpp16
-rw-r--r--src/test/fuzz/coinscache_sim.cpp8
-rw-r--r--src/test/fuzz/crypto_chacha20.cpp24
-rw-r--r--src/test/fuzz/crypto_chacha20poly1305.cpp200
-rw-r--r--src/test/fuzz/descriptor_parse.cpp12
-rw-r--r--src/test/fuzz/fuzz.cpp11
-rw-r--r--src/test/fuzz/hex.cpp8
-rw-r--r--src/test/fuzz/integer.cpp4
-rw-r--r--src/test/fuzz/kitchen_sink.cpp2
-rw-r--r--src/test/fuzz/mini_miner.cpp6
-rw-r--r--src/test/fuzz/miniscript.cpp4
-rw-r--r--src/test/fuzz/muhash.cpp6
-rw-r--r--src/test/fuzz/net.cpp12
-rw-r--r--src/test/fuzz/p2p_handshake.cpp107
-rw-r--r--src/test/fuzz/p2p_transport_serialization.cpp17
-rw-r--r--src/test/fuzz/parse_univalue.cpp4
-rw-r--r--src/test/fuzz/poolresource.cpp37
-rw-r--r--src/test/fuzz/prevector.cpp14
-rw-r--r--src/test/fuzz/process_message.cpp2
-rw-r--r--src/test/fuzz/process_messages.cpp2
-rw-r--r--src/test/fuzz/random.cpp1
-rw-r--r--src/test/fuzz/rpc.cpp2
-rw-r--r--src/test/fuzz/script.cpp1
-rw-r--r--src/test/fuzz/script_sigcache.cpp20
-rw-r--r--src/test/fuzz/string.cpp3
-rw-r--r--src/test/fuzz/txorphan.cpp8
-rw-r--r--src/test/fuzz/util.cpp3
-rw-r--r--src/test/fuzz/util/descriptor.cpp59
-rw-r--r--src/test/fuzz/util/descriptor.h21
-rw-r--r--src/test/fuzz/util/net.cpp4
-rw-r--r--src/test/fuzz/utxo_snapshot.cpp149
-rw-r--r--src/test/fuzz/utxo_total_supply.cpp2
-rw-r--r--src/test/fuzz/vecdeque.cpp22
-rw-r--r--src/test/hash_tests.cpp2
-rw-r--r--src/test/i2p_tests.cpp4
-rw-r--r--src/test/key_tests.cpp41
-rw-r--r--src/test/merkleblock_tests.cpp6
-rw-r--r--src/test/miniscript_tests.cpp12
-rw-r--r--src/test/net_peer_connection_tests.cpp2
-rw-r--r--src/test/net_peer_eviction_tests.cpp4
-rw-r--r--src/test/orphanage_tests.cpp56
-rw-r--r--src/test/pmt_tests.cpp4
-rw-r--r--src/test/policyestimator_tests.cpp5
-rw-r--r--src/test/pow_tests.cpp13
-rw-r--r--src/test/prevector_tests.cpp19
-rw-r--r--src/test/random_tests.cpp149
-rw-r--r--src/test/script_p2sh_tests.cpp3
-rw-r--r--src/test/script_standard_tests.cpp28
-rw-r--r--src/test/script_tests.cpp34
-rw-r--r--src/test/serfloat_tests.cpp2
-rw-r--r--src/test/streams_tests.cpp5
-rw-r--r--src/test/transaction_tests.cpp19
-rw-r--r--src/test/txpackage_tests.cpp21
-rw-r--r--src/test/txrequest_tests.cpp4
-rw-r--r--src/test/txvalidationcache_tests.cpp38
-rw-r--r--src/test/uint256_tests.cpp379
-rw-r--r--src/test/util/chainstate.h2
-rw-r--r--src/test/util/cluster_linearize.h353
-rw-r--r--src/test/util/net.cpp31
-rw-r--r--src/test/util/net.h5
-rw-r--r--src/test/util/random.cpp31
-rw-r--r--src/test/util/random.h20
-rw-r--r--src/test/util/setup_common.cpp114
-rw-r--r--src/test/util/setup_common.h37
-rw-r--r--src/test/util/xoroshiro128plusplus.h71
-rw-r--r--src/test/util_tests.cpp10
-rw-r--r--src/test/validation_chainstatemanager_tests.cpp11
-rw-r--r--src/test/validation_tests.cpp6
-rw-r--r--src/test/versionbits_tests.cpp2
-rw-r--r--src/test/xoroshiro128plusplus_tests.cpp29
90 files changed, 3343 insertions, 783 deletions
diff --git a/src/test/argsman_tests.cpp b/src/test/argsman_tests.cpp
index 5f0318e8c4..297595a9cf 100644
--- a/src/test/argsman_tests.cpp
+++ b/src/test/argsman_tests.cpp
@@ -644,10 +644,12 @@ BOOST_AUTO_TEST_CASE(util_GetChainTypeString)
{
TestArgsManager test_args;
const auto testnet = std::make_pair("-testnet", ArgsManager::ALLOW_ANY);
+ const auto testnet4 = std::make_pair("-testnet4", ArgsManager::ALLOW_ANY);
const auto regtest = std::make_pair("-regtest", ArgsManager::ALLOW_ANY);
- test_args.SetupArgs({testnet, regtest});
+ test_args.SetupArgs({testnet, testnet4, regtest});
const char* argv_testnet[] = {"cmd", "-testnet"};
+ const char* argv_testnet4[] = {"cmd", "-testnet4"};
const char* argv_regtest[] = {"cmd", "-regtest"};
const char* argv_test_no_reg[] = {"cmd", "-testnet", "-noregtest"};
const char* argv_both[] = {"cmd", "-testnet", "-regtest"};
@@ -663,6 +665,12 @@ BOOST_AUTO_TEST_CASE(util_GetChainTypeString)
BOOST_CHECK(test_args.ParseParameters(2, argv_testnet, error));
BOOST_CHECK_EQUAL(test_args.GetChainTypeString(), "test");
+ BOOST_CHECK(test_args.ParseParameters(0, argv_testnet4, error));
+ BOOST_CHECK_EQUAL(test_args.GetChainTypeString(), "main");
+
+ BOOST_CHECK(test_args.ParseParameters(2, argv_testnet4, error));
+ BOOST_CHECK_EQUAL(test_args.GetChainTypeString(), "testnet4");
+
BOOST_CHECK(test_args.ParseParameters(2, argv_regtest, error));
BOOST_CHECK_EQUAL(test_args.GetChainTypeString(), "regtest");
@@ -758,8 +766,8 @@ struct ArgsMergeTestingSetup : public BasicTestingSetup {
ForEachNoDup(conf_actions, SET, SECTION_NEGATE, [&] {
for (bool soft_set : {false, true}) {
for (bool force_set : {false, true}) {
- for (const std::string& section : {ChainTypeToString(ChainType::MAIN), ChainTypeToString(ChainType::TESTNET), ChainTypeToString(ChainType::SIGNET)}) {
- for (const std::string& network : {ChainTypeToString(ChainType::MAIN), ChainTypeToString(ChainType::TESTNET), ChainTypeToString(ChainType::SIGNET)}) {
+ for (const std::string& section : {ChainTypeToString(ChainType::MAIN), ChainTypeToString(ChainType::TESTNET), ChainTypeToString(ChainType::TESTNET4), ChainTypeToString(ChainType::SIGNET)}) {
+ for (const std::string& network : {ChainTypeToString(ChainType::MAIN), ChainTypeToString(ChainType::TESTNET), ChainTypeToString(ChainType::TESTNET4), ChainTypeToString(ChainType::SIGNET)}) {
for (bool net_specific : {false, true}) {
fn(arg_actions, conf_actions, soft_set, force_set, section, network, net_specific);
}
@@ -913,7 +921,7 @@ BOOST_FIXTURE_TEST_CASE(util_ArgsMerge, ArgsMergeTestingSetup)
// Results file is formatted like:
//
// <input> || <IsArgSet/IsArgNegated/GetArg output> | <GetArgs output> | <GetUnsuitable output>
- BOOST_CHECK_EQUAL(out_sha_hex, "d1e436c1cd510d0ec44d5205d4b4e3bee6387d316e0075c58206cb16603f3d82");
+ BOOST_CHECK_EQUAL(out_sha_hex, "f1ee5ab094cc43d16a6086fa7f2c10389e0f99902616b31bbf29189972ad1473");
}
// Similar test as above, but for ArgsManager::GetChainTypeString function.
@@ -1016,7 +1024,7 @@ BOOST_FIXTURE_TEST_CASE(util_ChainMerge, ChainMergeTestingSetup)
// Results file is formatted like:
//
// <input> || <output>
- BOOST_CHECK_EQUAL(out_sha_hex, "f263493e300023b6509963887444c41386f44b63bc30047eb8402e8c1144854c");
+ BOOST_CHECK_EQUAL(out_sha_hex, "9e60306e1363528bbc19a47f22bcede88e5d6815212f18ec8e6cdc4638dddab4");
}
BOOST_AUTO_TEST_CASE(util_ReadWriteSettings)
diff --git a/src/test/arith_uint256_tests.cpp b/src/test/arith_uint256_tests.cpp
index 10028c7c93..f178499299 100644
--- a/src/test/arith_uint256_tests.cpp
+++ b/src/test/arith_uint256_tests.cpp
@@ -3,6 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <arith_uint256.h>
+#include <test/util/setup_common.h>
#include <uint256.h>
#include <boost/test/unit_test.hpp>
@@ -22,7 +23,8 @@ static inline arith_uint256 arith_uint256V(const std::vector<unsigned char>& vch
{
return UintToArith256(uint256(vch));
}
-static inline arith_uint256 arith_uint256S(const std::string& str) { return UintToArith256(uint256S(str)); }
+// Takes a number written in hex (with most significant digits first).
+static inline arith_uint256 arith_uint256S(std::string_view str) { return UintToArith256(uint256S(str)); }
const unsigned char R1Array[] =
"\x9c\x52\x4a\xdb\xcf\x56\x11\x12\x2b\x29\x12\x5e\x5d\x35\xd2\xd2"
@@ -104,6 +106,7 @@ BOOST_AUTO_TEST_CASE( basics ) // constructors, equality, inequality
BOOST_CHECK(arith_uint256S(R1L.ToString()) == R1L);
BOOST_CHECK(arith_uint256S(" 0x" + R1L.ToString() + " ") == R1L);
BOOST_CHECK(arith_uint256S("") == ZeroL);
+ BOOST_CHECK(arith_uint256S("1") == OneL);
BOOST_CHECK(R1L == arith_uint256S(R1ArrayHex));
BOOST_CHECK(arith_uint256(R1L) == R1L);
BOOST_CHECK((arith_uint256(R1L^R2L)^R2L) == R1L);
@@ -278,6 +281,12 @@ BOOST_AUTO_TEST_CASE( comparison ) // <= >= < >
BOOST_CHECK( R1L <= TmpL ); BOOST_CHECK( (R1L == TmpL) != (R1L < TmpL)); BOOST_CHECK( (TmpL == R1L) || !( R1L >= TmpL));
BOOST_CHECK(! (TmpL < R1L)); BOOST_CHECK(! (R1L > TmpL));
}
+
+ BOOST_CHECK_LT(ZeroL,
+ OneL);
+ // Verify hex number representation has the most significant digits first.
+ BOOST_CHECK_LT(arith_uint256S("0000000000000000000000000000000000000000000000000000000000000001"),
+ arith_uint256S("1000000000000000000000000000000000000000000000000000000000000000"));
}
BOOST_AUTO_TEST_CASE( plusMinus )
diff --git a/src/test/blockchain_tests.cpp b/src/test/blockchain_tests.cpp
index 9b8f419290..4ecc15041c 100644
--- a/src/test/blockchain_tests.cpp
+++ b/src/test/blockchain_tests.cpp
@@ -5,7 +5,9 @@
#include <boost/test/unit_test.hpp>
#include <chain.h>
+#include <node/blockstorage.h>
#include <rpc/blockchain.h>
+#include <sync.h>
#include <test/util/setup_common.h>
#include <util/string.h>
@@ -76,4 +78,43 @@ BOOST_AUTO_TEST_CASE(get_difficulty_for_very_high_target)
TestDifficulty(0x12345678, 5913134931067755359633408.0);
}
+//! Prune chain from height down to genesis block and check that
+//! GetPruneHeight returns the correct value
+static void CheckGetPruneHeight(node::BlockManager& blockman, CChain& chain, int height) EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
+{
+ AssertLockHeld(::cs_main);
+
+ // Emulate pruning all blocks from `height` down to the genesis block
+ // by unsetting the `BLOCK_HAVE_DATA` flag from `nStatus`
+ for (CBlockIndex* it{chain[height]}; it != nullptr && it->nHeight > 0; it = it->pprev) {
+ it->nStatus &= ~BLOCK_HAVE_DATA;
+ }
+
+ const auto prune_height{GetPruneHeight(blockman, chain)};
+ BOOST_REQUIRE(prune_height.has_value());
+ BOOST_CHECK_EQUAL(*prune_height, height);
+}
+
+BOOST_FIXTURE_TEST_CASE(get_prune_height, TestChain100Setup)
+{
+ LOCK(::cs_main);
+ auto& chain = m_node.chainman->ActiveChain();
+ auto& blockman = m_node.chainman->m_blockman;
+
+ // Fresh chain of 100 blocks without any pruned blocks, so std::nullopt should be returned
+ BOOST_CHECK(!GetPruneHeight(blockman, chain).has_value());
+
+ // Start pruning
+ CheckGetPruneHeight(blockman, chain, 1);
+ CheckGetPruneHeight(blockman, chain, 99);
+ CheckGetPruneHeight(blockman, chain, 100);
+}
+
+BOOST_AUTO_TEST_CASE(num_chain_tx_max)
+{
+ CBlockIndex block_index{};
+ block_index.m_chain_tx_count = std::numeric_limits<uint64_t>::max();
+ BOOST_CHECK_EQUAL(block_index.m_chain_tx_count, std::numeric_limits<uint64_t>::max());
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/blockfilter_tests.cpp b/src/test/blockfilter_tests.cpp
index b372f25ea9..470fdde30a 100644
--- a/src/test/blockfilter_tests.cpp
+++ b/src/test/blockfilter_tests.cpp
@@ -149,8 +149,7 @@ BOOST_AUTO_TEST_CASE(blockfilters_json_test)
unsigned int pos = 0;
/*int block_height =*/ test[pos++].getInt<int>();
- uint256 block_hash;
- BOOST_CHECK(ParseHashStr(test[pos++].get_str(), block_hash));
+ BOOST_CHECK(uint256::FromHex(test[pos++].get_str()));
CBlock block;
BOOST_REQUIRE(DecodeHexBlk(block, test[pos++].get_str()));
@@ -165,11 +164,9 @@ BOOST_AUTO_TEST_CASE(blockfilters_json_test)
tx_undo.vprevout.emplace_back(txout, 0, false);
}
- uint256 prev_filter_header_basic;
- BOOST_CHECK(ParseHashStr(test[pos++].get_str(), prev_filter_header_basic));
+ uint256 prev_filter_header_basic{*Assert(uint256::FromHex(test[pos++].get_str()))};
std::vector<unsigned char> filter_basic = ParseHex(test[pos++].get_str());
- uint256 filter_header_basic;
- BOOST_CHECK(ParseHashStr(test[pos++].get_str(), filter_header_basic));
+ uint256 filter_header_basic{*Assert(uint256::FromHex(test[pos++].get_str()))};
BlockFilter computed_filter_basic(BlockFilterType::BASIC, block, block_undo);
BOOST_CHECK(computed_filter_basic.GetFilter().GetEncoded() == filter_basic);
diff --git a/src/test/blockmanager_tests.cpp b/src/test/blockmanager_tests.cpp
index 9eb7acc3ca..121f00bd25 100644
--- a/src/test/blockmanager_tests.cpp
+++ b/src/test/blockmanager_tests.cpp
@@ -2,6 +2,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <chain.h>
#include <chainparams.h>
#include <clientversion.h>
#include <node/blockstorage.h>
@@ -113,7 +114,7 @@ BOOST_FIXTURE_TEST_CASE(blockmanager_block_data_availability, TestChain100Setup)
};
// 1) Return genesis block when all blocks are available
- BOOST_CHECK_EQUAL(blockman.GetFirstStoredBlock(tip), chainman->ActiveChain()[0]);
+ BOOST_CHECK_EQUAL(blockman.GetFirstBlock(tip, BLOCK_HAVE_DATA), chainman->ActiveChain()[0]);
BOOST_CHECK(blockman.CheckBlockDataAvailability(tip, *chainman->ActiveChain()[0]));
// 2) Check lower_block when all blocks are available
@@ -127,7 +128,7 @@ BOOST_FIXTURE_TEST_CASE(blockmanager_block_data_availability, TestChain100Setup)
func_prune_blocks(last_pruned_block);
// 3) The last block not pruned is in-between upper-block and the genesis block
- BOOST_CHECK_EQUAL(blockman.GetFirstStoredBlock(tip), first_available_block);
+ BOOST_CHECK_EQUAL(blockman.GetFirstBlock(tip, BLOCK_HAVE_DATA), first_available_block);
BOOST_CHECK(blockman.CheckBlockDataAvailability(tip, *first_available_block));
BOOST_CHECK(!blockman.CheckBlockDataAvailability(tip, *last_pruned_block));
}
diff --git a/src/test/bloom_tests.cpp b/src/test/bloom_tests.cpp
index cbf85277a8..0c6e2e752d 100644
--- a/src/test/bloom_tests.cpp
+++ b/src/test/bloom_tests.cpp
@@ -112,7 +112,7 @@ BOOST_AUTO_TEST_CASE(bloom_match)
CTransaction spendingTx(deserialize, TX_WITH_WITNESS, spendStream);
CBloomFilter filter(10, 0.000001, 0, BLOOM_UPDATE_ALL);
- filter.insert(uint256S("0xb4749f017444b051c44dfd2720e88f314ff94f3dd6d56d40ef65854fcd7fff6b"));
+ filter.insert(uint256{"b4749f017444b051c44dfd2720e88f314ff94f3dd6d56d40ef65854fcd7fff6b"});
BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(tx), "Simple Bloom filter didn't match tx hash");
filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL);
@@ -138,11 +138,11 @@ BOOST_AUTO_TEST_CASE(bloom_match)
BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(tx), "Simple Bloom filter didn't match output address");
filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL);
- filter.insert(COutPoint(TxidFromString("0x90c122d70786e899529d71dbeba91ba216982fb6ba58f3bdaab65e73b7e9260b"), 0));
+ filter.insert(COutPoint(Txid::FromHex("90c122d70786e899529d71dbeba91ba216982fb6ba58f3bdaab65e73b7e9260b").value(), 0));
BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(tx), "Simple Bloom filter didn't match COutPoint");
filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL);
- COutPoint prevOutPoint(TxidFromString("0x90c122d70786e899529d71dbeba91ba216982fb6ba58f3bdaab65e73b7e9260b"), 0);
+ COutPoint prevOutPoint(Txid::FromHex("90c122d70786e899529d71dbeba91ba216982fb6ba58f3bdaab65e73b7e9260b").value(), 0);
{
std::vector<unsigned char> data(32 + sizeof(unsigned int));
memcpy(data.data(), prevOutPoint.hash.begin(), 32);
@@ -152,7 +152,7 @@ BOOST_AUTO_TEST_CASE(bloom_match)
BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(tx), "Simple Bloom filter didn't match manually serialized COutPoint");
filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL);
- filter.insert(uint256S("00000009e784f32f62ef849763d4f45b98e07ba658647343b915ff832b110436"));
+ filter.insert(uint256{"00000009e784f32f62ef849763d4f45b98e07ba658647343b915ff832b110436"});
BOOST_CHECK_MESSAGE(!filter.IsRelevantAndUpdate(tx), "Simple Bloom filter matched random tx hash");
filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL);
@@ -160,11 +160,11 @@ BOOST_AUTO_TEST_CASE(bloom_match)
BOOST_CHECK_MESSAGE(!filter.IsRelevantAndUpdate(tx), "Simple Bloom filter matched random address");
filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL);
- filter.insert(COutPoint(TxidFromString("0x90c122d70786e899529d71dbeba91ba216982fb6ba58f3bdaab65e73b7e9260b"), 1));
+ filter.insert(COutPoint(Txid::FromHex("90c122d70786e899529d71dbeba91ba216982fb6ba58f3bdaab65e73b7e9260b").value(), 1));
BOOST_CHECK_MESSAGE(!filter.IsRelevantAndUpdate(tx), "Simple Bloom filter matched COutPoint for an output we didn't care about");
filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL);
- filter.insert(COutPoint(TxidFromString("0x000000d70786e899529d71dbeba91ba216982fb6ba58f3bdaab65e73b7e9260b"), 0));
+ filter.insert(COutPoint(Txid::FromHex("000000d70786e899529d71dbeba91ba216982fb6ba58f3bdaab65e73b7e9260b").value(), 0));
BOOST_CHECK_MESSAGE(!filter.IsRelevantAndUpdate(tx), "Simple Bloom filter matched COutPoint for an output we didn't care about");
}
@@ -173,7 +173,7 @@ BOOST_AUTO_TEST_CASE(merkle_block_1)
CBlock block = getBlock13b8a();
CBloomFilter filter(10, 0.000001, 0, BLOOM_UPDATE_ALL);
// Match the last transaction
- filter.insert(uint256S("0x74d681e0e03bafa802c8aa084379aa98d9fcd632ddc2ed9782b586ec87451f20"));
+ filter.insert(uint256{"74d681e0e03bafa802c8aa084379aa98d9fcd632ddc2ed9782b586ec87451f20"});
CMerkleBlock merkleBlock(block, filter);
BOOST_CHECK_EQUAL(merkleBlock.header.GetHash().GetHex(), block.GetHash().GetHex());
@@ -181,7 +181,7 @@ BOOST_AUTO_TEST_CASE(merkle_block_1)
BOOST_CHECK_EQUAL(merkleBlock.vMatchedTxn.size(), 1U);
std::pair<unsigned int, uint256> pair = merkleBlock.vMatchedTxn[0];
- BOOST_CHECK(merkleBlock.vMatchedTxn[0].second == uint256S("0x74d681e0e03bafa802c8aa084379aa98d9fcd632ddc2ed9782b586ec87451f20"));
+ BOOST_CHECK(merkleBlock.vMatchedTxn[0].second == uint256{"74d681e0e03bafa802c8aa084379aa98d9fcd632ddc2ed9782b586ec87451f20"});
BOOST_CHECK(merkleBlock.vMatchedTxn[0].first == 8);
std::vector<uint256> vMatched;
@@ -192,7 +192,7 @@ BOOST_AUTO_TEST_CASE(merkle_block_1)
BOOST_CHECK(vMatched[i] == merkleBlock.vMatchedTxn[i].second);
// Also match the 8th transaction
- filter.insert(uint256S("0xdd1fd2a6fc16404faf339881a90adbde7f4f728691ac62e8f168809cdfae1053"));
+ filter.insert(uint256{"dd1fd2a6fc16404faf339881a90adbde7f4f728691ac62e8f168809cdfae1053"});
merkleBlock = CMerkleBlock(block, filter);
BOOST_CHECK(merkleBlock.header.GetHash() == block.GetHash());
@@ -200,7 +200,7 @@ BOOST_AUTO_TEST_CASE(merkle_block_1)
BOOST_CHECK(merkleBlock.vMatchedTxn[1] == pair);
- BOOST_CHECK(merkleBlock.vMatchedTxn[0].second == uint256S("0xdd1fd2a6fc16404faf339881a90adbde7f4f728691ac62e8f168809cdfae1053"));
+ BOOST_CHECK(merkleBlock.vMatchedTxn[0].second == uint256{"dd1fd2a6fc16404faf339881a90adbde7f4f728691ac62e8f168809cdfae1053"});
BOOST_CHECK(merkleBlock.vMatchedTxn[0].first == 7);
BOOST_CHECK(merkleBlock.txn.ExtractMatches(vMatched, vIndex) == block.hashMerkleRoot);
@@ -221,7 +221,7 @@ BOOST_AUTO_TEST_CASE(merkle_block_2)
CBloomFilter filter(10, 0.000001, 0, BLOOM_UPDATE_ALL);
// Match the first transaction
- filter.insert(uint256S("0xe980fe9f792d014e73b95203dc1335c5f9ce19ac537a419e6df5b47aecb93b70"));
+ filter.insert(uint256{"e980fe9f792d014e73b95203dc1335c5f9ce19ac537a419e6df5b47aecb93b70"});
CMerkleBlock merkleBlock(block, filter);
BOOST_CHECK(merkleBlock.header.GetHash() == block.GetHash());
@@ -229,7 +229,7 @@ BOOST_AUTO_TEST_CASE(merkle_block_2)
BOOST_CHECK(merkleBlock.vMatchedTxn.size() == 1);
std::pair<unsigned int, uint256> pair = merkleBlock.vMatchedTxn[0];
- BOOST_CHECK(merkleBlock.vMatchedTxn[0].second == uint256S("0xe980fe9f792d014e73b95203dc1335c5f9ce19ac537a419e6df5b47aecb93b70"));
+ BOOST_CHECK(merkleBlock.vMatchedTxn[0].second == uint256{"e980fe9f792d014e73b95203dc1335c5f9ce19ac537a419e6df5b47aecb93b70"});
BOOST_CHECK(merkleBlock.vMatchedTxn[0].first == 0);
std::vector<uint256> vMatched;
@@ -251,13 +251,13 @@ BOOST_AUTO_TEST_CASE(merkle_block_2)
BOOST_CHECK(pair == merkleBlock.vMatchedTxn[0]);
- BOOST_CHECK(merkleBlock.vMatchedTxn[1].second == uint256S("0x28204cad1d7fc1d199e8ef4fa22f182de6258a3eaafe1bbe56ebdcacd3069a5f"));
+ BOOST_CHECK(merkleBlock.vMatchedTxn[1].second == uint256{"28204cad1d7fc1d199e8ef4fa22f182de6258a3eaafe1bbe56ebdcacd3069a5f"});
BOOST_CHECK(merkleBlock.vMatchedTxn[1].first == 1);
- BOOST_CHECK(merkleBlock.vMatchedTxn[2].second == uint256S("0x6b0f8a73a56c04b519f1883e8aafda643ba61a30bd1439969df21bea5f4e27e2"));
+ BOOST_CHECK(merkleBlock.vMatchedTxn[2].second == uint256{"6b0f8a73a56c04b519f1883e8aafda643ba61a30bd1439969df21bea5f4e27e2"});
BOOST_CHECK(merkleBlock.vMatchedTxn[2].first == 2);
- BOOST_CHECK(merkleBlock.vMatchedTxn[3].second == uint256S("0x3c1d7e82342158e4109df2e0b6348b6e84e403d8b4046d7007663ace63cddb23"));
+ BOOST_CHECK(merkleBlock.vMatchedTxn[3].second == uint256{"3c1d7e82342158e4109df2e0b6348b6e84e403d8b4046d7007663ace63cddb23"});
BOOST_CHECK(merkleBlock.vMatchedTxn[3].first == 3);
BOOST_CHECK(merkleBlock.txn.ExtractMatches(vMatched, vIndex) == block.hashMerkleRoot);
@@ -278,7 +278,7 @@ BOOST_AUTO_TEST_CASE(merkle_block_2_with_update_none)
CBloomFilter filter(10, 0.000001, 0, BLOOM_UPDATE_NONE);
// Match the first transaction
- filter.insert(uint256S("0xe980fe9f792d014e73b95203dc1335c5f9ce19ac537a419e6df5b47aecb93b70"));
+ filter.insert(uint256{"e980fe9f792d014e73b95203dc1335c5f9ce19ac537a419e6df5b47aecb93b70"});
CMerkleBlock merkleBlock(block, filter);
BOOST_CHECK(merkleBlock.header.GetHash() == block.GetHash());
@@ -286,7 +286,7 @@ BOOST_AUTO_TEST_CASE(merkle_block_2_with_update_none)
BOOST_CHECK(merkleBlock.vMatchedTxn.size() == 1);
std::pair<unsigned int, uint256> pair = merkleBlock.vMatchedTxn[0];
- BOOST_CHECK(merkleBlock.vMatchedTxn[0].second == uint256S("0xe980fe9f792d014e73b95203dc1335c5f9ce19ac537a419e6df5b47aecb93b70"));
+ BOOST_CHECK(merkleBlock.vMatchedTxn[0].second == uint256{"e980fe9f792d014e73b95203dc1335c5f9ce19ac537a419e6df5b47aecb93b70"});
BOOST_CHECK(merkleBlock.vMatchedTxn[0].first == 0);
std::vector<uint256> vMatched;
@@ -308,10 +308,10 @@ BOOST_AUTO_TEST_CASE(merkle_block_2_with_update_none)
BOOST_CHECK(pair == merkleBlock.vMatchedTxn[0]);
- BOOST_CHECK(merkleBlock.vMatchedTxn[1].second == uint256S("0x28204cad1d7fc1d199e8ef4fa22f182de6258a3eaafe1bbe56ebdcacd3069a5f"));
+ BOOST_CHECK(merkleBlock.vMatchedTxn[1].second == uint256{"28204cad1d7fc1d199e8ef4fa22f182de6258a3eaafe1bbe56ebdcacd3069a5f"});
BOOST_CHECK(merkleBlock.vMatchedTxn[1].first == 1);
- BOOST_CHECK(merkleBlock.vMatchedTxn[2].second == uint256S("0x3c1d7e82342158e4109df2e0b6348b6e84e403d8b4046d7007663ace63cddb23"));
+ BOOST_CHECK(merkleBlock.vMatchedTxn[2].second == uint256{"3c1d7e82342158e4109df2e0b6348b6e84e403d8b4046d7007663ace63cddb23"});
BOOST_CHECK(merkleBlock.vMatchedTxn[2].first == 3);
BOOST_CHECK(merkleBlock.txn.ExtractMatches(vMatched, vIndex) == block.hashMerkleRoot);
@@ -332,14 +332,14 @@ BOOST_AUTO_TEST_CASE(merkle_block_3_and_serialize)
CBloomFilter filter(10, 0.000001, 0, BLOOM_UPDATE_ALL);
// Match the only transaction
- filter.insert(uint256S("0x63194f18be0af63f2c6bc9dc0f777cbefed3d9415c4af83f3ee3a3d669c00cb5"));
+ filter.insert(uint256{"63194f18be0af63f2c6bc9dc0f777cbefed3d9415c4af83f3ee3a3d669c00cb5"});
CMerkleBlock merkleBlock(block, filter);
BOOST_CHECK(merkleBlock.header.GetHash() == block.GetHash());
BOOST_CHECK(merkleBlock.vMatchedTxn.size() == 1);
- BOOST_CHECK(merkleBlock.vMatchedTxn[0].second == uint256S("0x63194f18be0af63f2c6bc9dc0f777cbefed3d9415c4af83f3ee3a3d669c00cb5"));
+ BOOST_CHECK(merkleBlock.vMatchedTxn[0].second == uint256{"63194f18be0af63f2c6bc9dc0f777cbefed3d9415c4af83f3ee3a3d669c00cb5"});
BOOST_CHECK(merkleBlock.vMatchedTxn[0].first == 0);
std::vector<uint256> vMatched;
@@ -370,7 +370,7 @@ BOOST_AUTO_TEST_CASE(merkle_block_4)
CBloomFilter filter(10, 0.000001, 0, BLOOM_UPDATE_ALL);
// Match the last transaction
- filter.insert(uint256S("0x0a2a92f0bda4727d0a13eaddf4dd9ac6b5c61a1429e6b2b818f19b15df0ac154"));
+ filter.insert(uint256{"0a2a92f0bda4727d0a13eaddf4dd9ac6b5c61a1429e6b2b818f19b15df0ac154"});
CMerkleBlock merkleBlock(block, filter);
BOOST_CHECK(merkleBlock.header.GetHash() == block.GetHash());
@@ -378,7 +378,7 @@ BOOST_AUTO_TEST_CASE(merkle_block_4)
BOOST_CHECK(merkleBlock.vMatchedTxn.size() == 1);
std::pair<unsigned int, uint256> pair = merkleBlock.vMatchedTxn[0];
- BOOST_CHECK(merkleBlock.vMatchedTxn[0].second == uint256S("0x0a2a92f0bda4727d0a13eaddf4dd9ac6b5c61a1429e6b2b818f19b15df0ac154"));
+ BOOST_CHECK(merkleBlock.vMatchedTxn[0].second == uint256{"0a2a92f0bda4727d0a13eaddf4dd9ac6b5c61a1429e6b2b818f19b15df0ac154"});
BOOST_CHECK(merkleBlock.vMatchedTxn[0].first == 6);
std::vector<uint256> vMatched;
@@ -389,13 +389,13 @@ BOOST_AUTO_TEST_CASE(merkle_block_4)
BOOST_CHECK(vMatched[i] == merkleBlock.vMatchedTxn[i].second);
// Also match the 4th transaction
- filter.insert(uint256S("0x02981fa052f0481dbc5868f4fc2166035a10f27a03cfd2de67326471df5bc041"));
+ filter.insert(uint256{"02981fa052f0481dbc5868f4fc2166035a10f27a03cfd2de67326471df5bc041"});
merkleBlock = CMerkleBlock(block, filter);
BOOST_CHECK(merkleBlock.header.GetHash() == block.GetHash());
BOOST_CHECK(merkleBlock.vMatchedTxn.size() == 2);
- BOOST_CHECK(merkleBlock.vMatchedTxn[0].second == uint256S("0x02981fa052f0481dbc5868f4fc2166035a10f27a03cfd2de67326471df5bc041"));
+ BOOST_CHECK(merkleBlock.vMatchedTxn[0].second == uint256{"02981fa052f0481dbc5868f4fc2166035a10f27a03cfd2de67326471df5bc041"});
BOOST_CHECK(merkleBlock.vMatchedTxn[0].first == 3);
BOOST_CHECK(merkleBlock.vMatchedTxn[1] == pair);
@@ -426,9 +426,9 @@ BOOST_AUTO_TEST_CASE(merkle_block_4_test_p2pubkey_only)
BOOST_CHECK(merkleBlock.header.GetHash() == block.GetHash());
// We should match the generation outpoint
- BOOST_CHECK(filter.contains(COutPoint(TxidFromString("0x147caa76786596590baa4e98f5d9f48b86c7765e489f7a6ff3360fe5c674360b"), 0)));
+ BOOST_CHECK(filter.contains(COutPoint(Txid::FromHex("147caa76786596590baa4e98f5d9f48b86c7765e489f7a6ff3360fe5c674360b").value(), 0)));
// ... but not the 4th transaction's output (its not pay-2-pubkey)
- BOOST_CHECK(!filter.contains(COutPoint(TxidFromString("0x02981fa052f0481dbc5868f4fc2166035a10f27a03cfd2de67326471df5bc041"), 0)));
+ BOOST_CHECK(!filter.contains(COutPoint(Txid::FromHex("02981fa052f0481dbc5868f4fc2166035a10f27a03cfd2de67326471df5bc041").value(), 0)));
}
BOOST_AUTO_TEST_CASE(merkle_block_4_test_update_none)
@@ -451,8 +451,8 @@ BOOST_AUTO_TEST_CASE(merkle_block_4_test_update_none)
BOOST_CHECK(merkleBlock.header.GetHash() == block.GetHash());
// We shouldn't match any outpoints (UPDATE_NONE)
- BOOST_CHECK(!filter.contains(COutPoint(TxidFromString("0x147caa76786596590baa4e98f5d9f48b86c7765e489f7a6ff3360fe5c674360b"), 0)));
- BOOST_CHECK(!filter.contains(COutPoint(TxidFromString("0x02981fa052f0481dbc5868f4fc2166035a10f27a03cfd2de67326471df5bc041"), 0)));
+ BOOST_CHECK(!filter.contains(COutPoint(Txid::FromHex("147caa76786596590baa4e98f5d9f48b86c7765e489f7a6ff3360fe5c674360b").value(), 0)));
+ BOOST_CHECK(!filter.contains(COutPoint(Txid::FromHex("02981fa052f0481dbc5868f4fc2166035a10f27a03cfd2de67326471df5bc041").value(), 0)));
}
static std::vector<unsigned char> RandomData()
@@ -463,8 +463,7 @@ static std::vector<unsigned char> RandomData()
BOOST_AUTO_TEST_CASE(rolling_bloom)
{
- SeedInsecureRand(SeedRand::ZEROS);
- g_mock_deterministic_tests = true;
+ SeedRandomForTest(SeedRand::ZEROS);
// last-100-entry, 1% false positive:
CRollingBloomFilter rb1(100, 0.01);
@@ -491,7 +490,7 @@ BOOST_AUTO_TEST_CASE(rolling_bloom)
++nHits;
}
// Expect about 100 hits
- BOOST_CHECK_EQUAL(nHits, 75U);
+ BOOST_CHECK_EQUAL(nHits, 71U);
BOOST_CHECK(rb1.contains(data[DATASIZE-1]));
rb1.reset();
@@ -519,7 +518,7 @@ BOOST_AUTO_TEST_CASE(rolling_bloom)
++nHits;
}
// Expect about 5 false positives
- BOOST_CHECK_EQUAL(nHits, 6U);
+ BOOST_CHECK_EQUAL(nHits, 3U);
// last-1000-entry, 0.01% false positive:
CRollingBloomFilter rb2(1000, 0.001);
@@ -530,7 +529,6 @@ BOOST_AUTO_TEST_CASE(rolling_bloom)
for (int i = 0; i < DATASIZE; i++) {
BOOST_CHECK(rb2.contains(data[i]));
}
- g_mock_deterministic_tests = false;
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/checkqueue_tests.cpp b/src/test/checkqueue_tests.cpp
index 023a5e8e70..7810d91a77 100644
--- a/src/test/checkqueue_tests.cpp
+++ b/src/test/checkqueue_tests.cpp
@@ -28,7 +28,7 @@
struct NoLockLoggingTestingSetup : public TestingSetup {
NoLockLoggingTestingSetup()
#ifdef DEBUG_LOCKCONTENTION
- : TestingSetup{ChainType::MAIN, /*extra_args=*/{"-debugexclude=lock"}} {}
+ : TestingSetup{ChainType::MAIN, {.extra_args = { "-debugexclude=lock" } }} {}
#else
: TestingSetup{ChainType::MAIN} {}
#endif
diff --git a/src/test/cluster_linearize_tests.cpp b/src/test/cluster_linearize_tests.cpp
new file mode 100644
index 0000000000..d15e783ea1
--- /dev/null
+++ b/src/test/cluster_linearize_tests.cpp
@@ -0,0 +1,138 @@
+// Copyright (c) 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 <cluster_linearize.h>
+#include <test/util/cluster_linearize.h>
+#include <test/util/setup_common.h>
+#include <util/bitset.h>
+#include <util/strencodings.h>
+
+#include <vector>
+
+#include <boost/test/unit_test.hpp>
+
+BOOST_FIXTURE_TEST_SUITE(cluster_linearize_tests, BasicTestingSetup)
+
+using namespace cluster_linearize;
+
+namespace {
+
+template<typename SetType>
+void TestDepGraphSerialization(const Cluster<SetType>& cluster, const std::string& hexenc)
+{
+ DepGraph depgraph(cluster);
+
+ // Run normal sanity and correspondence checks, which includes a round-trip test.
+ VerifyDepGraphFromCluster(cluster, depgraph);
+
+ // There may be multiple serializations of the same graph, but DepGraphFormatter's serializer
+ // only produces one of those. Verify that hexenc matches that canonical serialization.
+ std::vector<unsigned char> encoding;
+ VectorWriter writer(encoding, 0);
+ writer << Using<DepGraphFormatter>(depgraph);
+ BOOST_CHECK_EQUAL(HexStr(encoding), hexenc);
+
+ // Test that deserializing that encoding yields depgraph. This is effectively already implied
+ // by the round-trip test above (if depgraph is acyclic), but verify it explicitly again here.
+ SpanReader reader(encoding);
+ DepGraph<SetType> depgraph_read;
+ reader >> Using<DepGraphFormatter>(depgraph_read);
+ BOOST_CHECK(depgraph == depgraph_read);
+}
+
+} // namespace
+
+BOOST_AUTO_TEST_CASE(depgraph_ser_tests)
+{
+ // Empty cluster.
+ TestDepGraphSerialization<TestBitSet>(
+ {},
+ "00" /* end of graph */);
+
+ // Transactions: A(fee=0,size=1).
+ TestDepGraphSerialization<TestBitSet>(
+ {{{0, 1}, {}}},
+ "01" /* A size */
+ "00" /* A fee */
+ "00" /* A insertion position (no skips): A */
+ "00" /* end of graph */);
+
+ // Transactions: A(fee=42,size=11), B(fee=-13,size=7), B depends on A.
+ TestDepGraphSerialization<TestBitSet>(
+ {{{42, 11}, {}}, {{-13, 7}, {0}}},
+ "0b" /* A size */
+ "54" /* A fee */
+ "00" /* A insertion position (no skips): A */
+ "07" /* B size */
+ "19" /* B fee */
+ "00" /* B->A dependency (no skips) */
+ "00" /* B insertion position (no skips): A,B */
+ "00" /* end of graph */);
+
+ // Transactions: A(64,128), B(128,256), C(1,1), C depends on A and B.
+ TestDepGraphSerialization<TestBitSet>(
+ {{{64, 128}, {}}, {{128, 256}, {}}, {{1, 1}, {0, 1}}},
+ "8000" /* A size */
+ "8000" /* A fee */
+ "00" /* A insertion position (no skips): A */
+ "8100" /* B size */
+ "8100" /* B fee */
+ "01" /* B insertion position (skip B->A dependency): A,B */
+ "01" /* C size */
+ "02" /* C fee */
+ "00" /* C->B dependency (no skips) */
+ "00" /* C->A dependency (no skips) */
+ "00" /* C insertion position (no skips): A,B,C */
+ "00" /* end of graph */);
+
+ // Transactions: A(-57,113), B(57,114), C(-58,115), D(58,116). Deps: B->A, C->A, D->C, in order
+ // [B,A,C,D]. This exercises non-topological ordering (internally serialized as A,B,C,D).
+ TestDepGraphSerialization<TestBitSet>(
+ {{{57, 114}, {1}}, {{-57, 113}, {}}, {{-58, 115}, {1}}, {{58, 116}, {2}}},
+ "71" /* A size */
+ "71" /* A fee */
+ "00" /* A insertion position (no skips): A */
+ "72" /* B size */
+ "72" /* B fee */
+ "00" /* B->A dependency (no skips) */
+ "01" /* B insertion position (skip A): B,A */
+ "73" /* C size */
+ "73" /* C fee */
+ "01" /* C->A dependency (skip C->B dependency) */
+ "00" /* C insertion position (no skips): B,A,C */
+ "74" /* D size */
+ "74" /* D fee */
+ "00" /* D->C dependency (no skips) */
+ "01" /* D insertion position (skip D->B dependency, D->A is implied): B,A,C,D */
+ "00" /* end of graph */);
+
+ // Transactions: A(1,2), B(3,1), C(2,1), D(1,3), E(1,1). Deps: C->A, D->A, D->B, E->D.
+ // In order: [D,A,B,E,C]. Internally serialized in order A,B,C,D,E.
+ TestDepGraphSerialization<TestBitSet>(
+ {{{1, 3}, {1, 2}}, {{1, 2}, {}}, {{3, 1}, {}}, {{1, 1}, {0}}, {{2, 1}, {1}}},
+ "02" /* A size */
+ "02" /* A fee */
+ "00" /* A insertion position (no skips): A */
+ "01" /* B size */
+ "06" /* B fee */
+ "01" /* B insertion position (skip B->A dependency): A,B */
+ "01" /* C size */
+ "04" /* C fee */
+ "01" /* C->A dependency (skip C->B dependency) */
+ "00" /* C insertion position (no skips): A,B,C */
+ "03" /* D size */
+ "02" /* D fee */
+ "01" /* D->B dependency (skip D->C dependency) */
+ "00" /* D->A dependency (no skips) */
+ "03" /* D insertion position (skip C,B,A): D,A,B,C */
+ "01" /* E size */
+ "02" /* E fee */
+ "00" /* E->D dependency (no skips) */
+ "02" /* E insertion position (skip E->C dependency, E->B and E->A are implied,
+ skip insertion C): D,A,B,E,C */
+ "00" /* end of graph */
+ );
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/coins_tests.cpp b/src/test/coins_tests.cpp
index b6d3e7d567..fb929a2b0e 100644
--- a/src/test/coins_tests.cpp
+++ b/src/test/coins_tests.cpp
@@ -55,10 +55,10 @@ public:
uint256 GetBestBlock() const override { return hashBestBlock_; }
- bool BatchWrite(CCoinsMap& mapCoins, const uint256& hashBlock, bool erase = true) override
+ bool BatchWrite(CoinsViewCacheCursor& cursor, const uint256& hashBlock) override
{
- for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end(); it = erase ? mapCoins.erase(it) : std::next(it)) {
- if (it->second.flags & CCoinsCacheEntry::DIRTY) {
+ for (auto it{cursor.Begin()}; it != cursor.End(); it = cursor.NextAndMaybeErase(*it)){
+ if (it->second.IsDirty()) {
// Same optimization used in CCoinsViewDB is to only write dirty entries.
map_[it->first] = it->second.coin;
if (it->second.coin.IsSpent() && InsecureRandRange(3) == 0) {
@@ -78,7 +78,7 @@ class CCoinsViewCacheTest : public CCoinsViewCache
public:
explicit CCoinsViewCacheTest(CCoinsView* _base) : CCoinsViewCache(_base) {}
- void SelfTest() const
+ void SelfTest(bool sanity_check = true) const
{
// Manually recompute the dynamic usage of the whole data, and compare it.
size_t ret = memusage::DynamicUsage(cacheCoins);
@@ -89,9 +89,13 @@ public:
}
BOOST_CHECK_EQUAL(GetCacheSize(), count);
BOOST_CHECK_EQUAL(DynamicMemoryUsage(), ret);
+ if (sanity_check) {
+ SanityCheck();
+ }
}
CCoinsMap& map() const { return cacheCoins; }
+ CoinsCachePair& sentinel() const { return m_sentinel; }
size_t& usage() const { return cachedCoinsUsage; }
};
@@ -307,8 +311,7 @@ UtxoData::iterator FindRandomFrom(const std::set<COutPoint> &utxoSet) {
// has the expected effect (the other duplicate is overwritten at all cache levels)
BOOST_AUTO_TEST_CASE(updatecoins_simulation_test)
{
- SeedInsecureRand(SeedRand::ZEROS);
- g_mock_deterministic_tests = true;
+ SeedRandomForTest(SeedRand::ZEROS);
bool spent_a_duplicate_coinbase = false;
// A simple map to track what we expect the cache stack to represent.
@@ -496,8 +499,6 @@ BOOST_AUTO_TEST_CASE(updatecoins_simulation_test)
// Verify coverage.
BOOST_CHECK(spent_a_duplicate_coinbase);
-
- g_mock_deterministic_tests = false;
}
BOOST_AUTO_TEST_CASE(ccoins_serialization)
@@ -579,7 +580,7 @@ static void SetCoinsValue(CAmount value, Coin& coin)
}
}
-static size_t InsertCoinsMapEntry(CCoinsMap& map, CAmount value, char flags)
+static size_t InsertCoinsMapEntry(CCoinsMap& map, CoinsCachePair& sentinel, CAmount value, char flags)
{
if (value == ABSENT) {
assert(flags == NO_ENTRY);
@@ -587,10 +588,10 @@ static size_t InsertCoinsMapEntry(CCoinsMap& map, CAmount value, char flags)
}
assert(flags != NO_ENTRY);
CCoinsCacheEntry entry;
- entry.flags = flags;
SetCoinsValue(value, entry.coin);
auto inserted = map.emplace(OUTPOINT, std::move(entry));
assert(inserted.second);
+ inserted.first->second.AddFlags(flags, *inserted.first, sentinel);
return inserted.first->second.coin.DynamicMemoryUsage();
}
@@ -606,17 +607,20 @@ void GetCoinsMapEntry(const CCoinsMap& map, CAmount& value, char& flags, const C
} else {
value = it->second.coin.out.nValue;
}
- flags = it->second.flags;
+ flags = it->second.GetFlags();
assert(flags != NO_ENTRY);
}
}
void WriteCoinsViewEntry(CCoinsView& view, CAmount value, char flags)
{
+ CoinsCachePair sentinel{};
+ sentinel.second.SelfRef(sentinel);
CCoinsMapMemoryResource resource;
CCoinsMap map{0, CCoinsMap::hasher{}, CCoinsMap::key_equal{}, &resource};
- InsertCoinsMapEntry(map, value, flags);
- BOOST_CHECK(view.BatchWrite(map, {}));
+ auto usage{InsertCoinsMapEntry(map, sentinel, value, flags)};
+ auto cursor{CoinsViewCacheCursor(usage, sentinel, map, /*will_erase=*/true)};
+ BOOST_CHECK(view.BatchWrite(cursor, {}));
}
class SingleEntryCacheTest
@@ -625,7 +629,7 @@ public:
SingleEntryCacheTest(CAmount base_value, CAmount cache_value, char cache_flags)
{
WriteCoinsViewEntry(base, base_value, base_value == ABSENT ? NO_ENTRY : DIRTY);
- cache.usage() += InsertCoinsMapEntry(cache.map(), cache_value, cache_flags);
+ cache.usage() += InsertCoinsMapEntry(cache.map(), cache.sentinel(), cache_value, cache_flags);
}
CCoinsView root;
@@ -637,7 +641,7 @@ static void CheckAccessCoin(CAmount base_value, CAmount cache_value, CAmount exp
{
SingleEntryCacheTest test(base_value, cache_value, cache_flags);
test.cache.AccessCoin(OUTPOINT);
- test.cache.SelfTest();
+ test.cache.SelfTest(/*sanity_check=*/false);
CAmount result_value;
char result_flags;
@@ -806,7 +810,7 @@ void CheckWriteCoins(CAmount parent_value, CAmount child_value, CAmount expected
char result_flags;
try {
WriteCoinsViewEntry(test.cache, child_value, child_flags);
- test.cache.SelfTest();
+ test.cache.SelfTest(/*sanity_check=*/false);
GetCoinsMapEntry(test.cache.map(), result_value, result_flags);
} catch (std::logic_error&) {
result_value = FAIL;
@@ -919,6 +923,7 @@ void TestFlushBehavior(
// Flush in reverse order to ensure that flushes happen from children up.
for (auto i = all_caches.rbegin(); i != all_caches.rend(); ++i) {
auto& cache = *i;
+ cache->SanityCheck();
// hashBlock must be filled before flushing to disk; value is
// unimportant here. This is normally done during connect/disconnect block.
cache->SetBestBlock(InsecureRand256());
diff --git a/src/test/coinscachepair_tests.cpp b/src/test/coinscachepair_tests.cpp
new file mode 100644
index 0000000000..61840f1f09
--- /dev/null
+++ b/src/test/coinscachepair_tests.cpp
@@ -0,0 +1,219 @@
+// Copyright (c) 2024-present 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 <coins.h>
+
+#include <boost/test/unit_test.hpp>
+
+#include <list>
+
+BOOST_AUTO_TEST_SUITE(coinscachepair_tests)
+
+static constexpr auto NUM_NODES{4};
+
+std::list<CoinsCachePair> CreatePairs(CoinsCachePair& sentinel)
+{
+ std::list<CoinsCachePair> nodes;
+ for (auto i{0}; i < NUM_NODES; ++i) {
+ nodes.emplace_back();
+
+ auto node{std::prev(nodes.end())};
+ node->second.AddFlags(CCoinsCacheEntry::DIRTY, *node, sentinel);
+
+ BOOST_CHECK_EQUAL(node->second.GetFlags(), CCoinsCacheEntry::DIRTY);
+ BOOST_CHECK_EQUAL(node->second.Next(), &sentinel);
+ BOOST_CHECK_EQUAL(sentinel.second.Prev(), &(*node));
+
+ if (i > 0) {
+ BOOST_CHECK_EQUAL(std::prev(node)->second.Next(), &(*node));
+ BOOST_CHECK_EQUAL(node->second.Prev(), &(*std::prev(node)));
+ }
+ }
+ return nodes;
+}
+
+BOOST_AUTO_TEST_CASE(linked_list_iteration)
+{
+ CoinsCachePair sentinel;
+ sentinel.second.SelfRef(sentinel);
+ auto nodes{CreatePairs(sentinel)};
+
+ // Check iterating through pairs is identical to iterating through a list
+ auto node{sentinel.second.Next()};
+ for (const auto& expected : nodes) {
+ BOOST_CHECK_EQUAL(&expected, node);
+ node = node->second.Next();
+ }
+ BOOST_CHECK_EQUAL(node, &sentinel);
+
+ // Check iterating through pairs is identical to iterating through a list
+ // Clear the flags during iteration
+ node = sentinel.second.Next();
+ for (const auto& expected : nodes) {
+ BOOST_CHECK_EQUAL(&expected, node);
+ auto next = node->second.Next();
+ node->second.ClearFlags();
+ node = next;
+ }
+ BOOST_CHECK_EQUAL(node, &sentinel);
+ // Check that sentinel's next and prev are itself
+ BOOST_CHECK_EQUAL(sentinel.second.Next(), &sentinel);
+ BOOST_CHECK_EQUAL(sentinel.second.Prev(), &sentinel);
+
+ // Delete the nodes from the list to make sure there are no dangling pointers
+ for (auto it{nodes.begin()}; it != nodes.end(); it = nodes.erase(it)) {
+ BOOST_CHECK_EQUAL(it->second.GetFlags(), 0);
+ }
+}
+
+BOOST_AUTO_TEST_CASE(linked_list_iterate_erase)
+{
+ CoinsCachePair sentinel;
+ sentinel.second.SelfRef(sentinel);
+ auto nodes{CreatePairs(sentinel)};
+
+ // Check iterating through pairs is identical to iterating through a list
+ // Erase the nodes as we iterate through, but don't clear flags
+ // The flags will be cleared by the CCoinsCacheEntry's destructor
+ auto node{sentinel.second.Next()};
+ for (auto expected{nodes.begin()}; expected != nodes.end(); expected = nodes.erase(expected)) {
+ BOOST_CHECK_EQUAL(&(*expected), node);
+ node = node->second.Next();
+ }
+ BOOST_CHECK_EQUAL(node, &sentinel);
+
+ // Check that sentinel's next and prev are itself
+ BOOST_CHECK_EQUAL(sentinel.second.Next(), &sentinel);
+ BOOST_CHECK_EQUAL(sentinel.second.Prev(), &sentinel);
+}
+
+BOOST_AUTO_TEST_CASE(linked_list_random_deletion)
+{
+ CoinsCachePair sentinel;
+ sentinel.second.SelfRef(sentinel);
+ auto nodes{CreatePairs(sentinel)};
+
+ // Create linked list sentinel->n1->n2->n3->n4->sentinel
+ auto n1{nodes.begin()};
+ auto n2{std::next(n1)};
+ auto n3{std::next(n2)};
+ auto n4{std::next(n3)};
+
+ // Delete n2
+ // sentinel->n1->n3->n4->sentinel
+ nodes.erase(n2);
+ // Check that n1 now points to n3, and n3 still points to n4
+ // Also check that flags were not altered
+ BOOST_CHECK_EQUAL(n1->second.GetFlags(), CCoinsCacheEntry::DIRTY);
+ BOOST_CHECK_EQUAL(n1->second.Next(), &(*n3));
+ BOOST_CHECK_EQUAL(n3->second.GetFlags(), CCoinsCacheEntry::DIRTY);
+ BOOST_CHECK_EQUAL(n3->second.Next(), &(*n4));
+ BOOST_CHECK_EQUAL(n3->second.Prev(), &(*n1));
+
+ // Delete n1
+ // sentinel->n3->n4->sentinel
+ nodes.erase(n1);
+ // Check that sentinel now points to n3, and n3 still points to n4
+ // Also check that flags were not altered
+ BOOST_CHECK_EQUAL(n3->second.GetFlags(), CCoinsCacheEntry::DIRTY);
+ BOOST_CHECK_EQUAL(sentinel.second.Next(), &(*n3));
+ BOOST_CHECK_EQUAL(n3->second.Next(), &(*n4));
+ BOOST_CHECK_EQUAL(n3->second.Prev(), &sentinel);
+
+ // Delete n4
+ // sentinel->n3->sentinel
+ nodes.erase(n4);
+ // Check that sentinel still points to n3, and n3 points to sentinel
+ // Also check that flags were not altered
+ BOOST_CHECK_EQUAL(n3->second.GetFlags(), CCoinsCacheEntry::DIRTY);
+ BOOST_CHECK_EQUAL(sentinel.second.Next(), &(*n3));
+ BOOST_CHECK_EQUAL(n3->second.Next(), &sentinel);
+ BOOST_CHECK_EQUAL(sentinel.second.Prev(), &(*n3));
+
+ // Delete n3
+ // sentinel->sentinel
+ nodes.erase(n3);
+ // Check that sentinel's next and prev are itself
+ BOOST_CHECK_EQUAL(sentinel.second.Next(), &sentinel);
+ BOOST_CHECK_EQUAL(sentinel.second.Prev(), &sentinel);
+}
+
+BOOST_AUTO_TEST_CASE(linked_list_add_flags)
+{
+ CoinsCachePair sentinel;
+ sentinel.second.SelfRef(sentinel);
+ CoinsCachePair n1;
+ CoinsCachePair n2;
+
+ // Check that adding 0 flag has no effect
+ n1.second.AddFlags(0, n1, sentinel);
+ BOOST_CHECK_EQUAL(n1.second.GetFlags(), 0);
+ BOOST_CHECK_EQUAL(sentinel.second.Next(), &sentinel);
+ BOOST_CHECK_EQUAL(sentinel.second.Prev(), &sentinel);
+
+ // Check that adding DIRTY flag inserts it into linked list and sets flags
+ n1.second.AddFlags(CCoinsCacheEntry::DIRTY, n1, sentinel);
+ BOOST_CHECK_EQUAL(n1.second.GetFlags(), CCoinsCacheEntry::DIRTY);
+ BOOST_CHECK_EQUAL(n1.second.Next(), &sentinel);
+ BOOST_CHECK_EQUAL(n1.second.Prev(), &sentinel);
+ BOOST_CHECK_EQUAL(sentinel.second.Next(), &n1);
+ BOOST_CHECK_EQUAL(sentinel.second.Prev(), &n1);
+
+ // Check that adding FRESH flag on new node inserts it after n1
+ n2.second.AddFlags(CCoinsCacheEntry::FRESH, n2, sentinel);
+ BOOST_CHECK_EQUAL(n2.second.GetFlags(), CCoinsCacheEntry::FRESH);
+ BOOST_CHECK_EQUAL(n2.second.Next(), &sentinel);
+ BOOST_CHECK_EQUAL(n2.second.Prev(), &n1);
+ BOOST_CHECK_EQUAL(n1.second.Next(), &n2);
+ BOOST_CHECK_EQUAL(sentinel.second.Prev(), &n2);
+
+ // Check that adding 0 flag has no effect, and doesn't change position
+ n1.second.AddFlags(0, n1, sentinel);
+ BOOST_CHECK_EQUAL(n1.second.GetFlags(), CCoinsCacheEntry::DIRTY);
+ BOOST_CHECK_EQUAL(n1.second.Next(), &n2);
+ BOOST_CHECK_EQUAL(n1.second.Prev(), &sentinel);
+ BOOST_CHECK_EQUAL(sentinel.second.Next(), &n1);
+ BOOST_CHECK_EQUAL(n2.second.Prev(), &n1);
+
+ // Check that we can add extra flags, but they don't change our position
+ n1.second.AddFlags(CCoinsCacheEntry::FRESH, n1, sentinel);
+ BOOST_CHECK_EQUAL(n1.second.GetFlags(), CCoinsCacheEntry::DIRTY | CCoinsCacheEntry::FRESH);
+ BOOST_CHECK_EQUAL(n1.second.Next(), &n2);
+ BOOST_CHECK_EQUAL(n1.second.Prev(), &sentinel);
+ BOOST_CHECK_EQUAL(sentinel.second.Next(), &n1);
+ BOOST_CHECK_EQUAL(n2.second.Prev(), &n1);
+
+ // Check that we can clear flags then re-add them
+ n1.second.ClearFlags();
+ BOOST_CHECK_EQUAL(n1.second.GetFlags(), 0);
+ BOOST_CHECK_EQUAL(sentinel.second.Next(), &n2);
+ BOOST_CHECK_EQUAL(sentinel.second.Prev(), &n2);
+ BOOST_CHECK_EQUAL(n2.second.Next(), &sentinel);
+ BOOST_CHECK_EQUAL(n2.second.Prev(), &sentinel);
+
+ // Check that calling `ClearFlags` with 0 flags has no effect
+ n1.second.ClearFlags();
+ BOOST_CHECK_EQUAL(n1.second.GetFlags(), 0);
+ BOOST_CHECK_EQUAL(sentinel.second.Next(), &n2);
+ BOOST_CHECK_EQUAL(sentinel.second.Prev(), &n2);
+ BOOST_CHECK_EQUAL(n2.second.Next(), &sentinel);
+ BOOST_CHECK_EQUAL(n2.second.Prev(), &sentinel);
+
+ // Adding 0 still has no effect
+ n1.second.AddFlags(0, n1, sentinel);
+ BOOST_CHECK_EQUAL(sentinel.second.Next(), &n2);
+ BOOST_CHECK_EQUAL(sentinel.second.Prev(), &n2);
+ BOOST_CHECK_EQUAL(n2.second.Next(), &sentinel);
+ BOOST_CHECK_EQUAL(n2.second.Prev(), &sentinel);
+
+ // But adding DIRTY re-inserts it after n2
+ n1.second.AddFlags(CCoinsCacheEntry::DIRTY, n1, sentinel);
+ BOOST_CHECK_EQUAL(n1.second.GetFlags(), CCoinsCacheEntry::DIRTY);
+ BOOST_CHECK_EQUAL(n2.second.Next(), &n1);
+ BOOST_CHECK_EQUAL(n1.second.Prev(), &n2);
+ BOOST_CHECK_EQUAL(n1.second.Next(), &sentinel);
+ BOOST_CHECK_EQUAL(sentinel.second.Prev(), &n1);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/crypto_tests.cpp b/src/test/crypto_tests.cpp
index 46acc6fc9f..f84e04e819 100644
--- a/src/test/crypto_tests.cpp
+++ b/src/test/crypto_tests.cpp
@@ -1195,7 +1195,7 @@ BOOST_AUTO_TEST_CASE(muhash_tests)
uint256 res;
int table[4];
for (int i = 0; i < 4; ++i) {
- table[i] = g_insecure_rand_ctx.randbits(3);
+ table[i] = g_insecure_rand_ctx.randbits<3>();
}
for (int order = 0; order < 4; ++order) {
MuHash3072 acc;
@@ -1215,8 +1215,8 @@ BOOST_AUTO_TEST_CASE(muhash_tests)
}
}
- MuHash3072 x = FromInt(g_insecure_rand_ctx.randbits(4)); // x=X
- MuHash3072 y = FromInt(g_insecure_rand_ctx.randbits(4)); // x=X, y=Y
+ MuHash3072 x = FromInt(g_insecure_rand_ctx.randbits<4>()); // x=X
+ MuHash3072 y = FromInt(g_insecure_rand_ctx.randbits<4>()); // x=X, y=Y
MuHash3072 z; // x=X, y=Y, z=1
z *= x; // x=X, y=Y, z=X
z *= y; // x=X, y=Y, z=X*Y
@@ -1235,7 +1235,7 @@ BOOST_AUTO_TEST_CASE(muhash_tests)
acc *= FromInt(1);
acc /= FromInt(2);
acc.Finalize(out);
- BOOST_CHECK_EQUAL(out, uint256S("10d312b100cbd32ada024a6646e40d3482fcff103668d2625f10002a607d5863"));
+ BOOST_CHECK_EQUAL(out, uint256{"10d312b100cbd32ada024a6646e40d3482fcff103668d2625f10002a607d5863"});
MuHash3072 acc2 = FromInt(0);
unsigned char tmp[32] = {1, 0};
@@ -1243,7 +1243,7 @@ BOOST_AUTO_TEST_CASE(muhash_tests)
unsigned char tmp2[32] = {2, 0};
acc2.Remove(tmp2);
acc2.Finalize(out);
- BOOST_CHECK_EQUAL(out, uint256S("10d312b100cbd32ada024a6646e40d3482fcff103668d2625f10002a607d5863"));
+ BOOST_CHECK_EQUAL(out, uint256{"10d312b100cbd32ada024a6646e40d3482fcff103668d2625f10002a607d5863"});
// Test MuHash3072 serialization
MuHash3072 serchk = FromInt(1); serchk *= FromInt(2);
diff --git a/src/test/cuckoocache_tests.cpp b/src/test/cuckoocache_tests.cpp
index eafbcf5681..fc22daeb57 100644
--- a/src/test/cuckoocache_tests.cpp
+++ b/src/test/cuckoocache_tests.cpp
@@ -33,11 +33,11 @@ BOOST_AUTO_TEST_SUITE(cuckoocache_tests);
/* Test that no values not inserted into the cache are read out of it.
*
- * There are no repeats in the first 200000 insecure_GetRandHash calls
+ * There are no repeats in the first 200000 InsecureRand256() calls
*/
BOOST_AUTO_TEST_CASE(test_cuckoocache_no_fakes)
{
- SeedInsecureRand(SeedRand::ZEROS);
+ SeedRandomForTest(SeedRand::ZEROS);
CuckooCache::cache<uint256, SignatureCacheHasher> cc{};
size_t megabytes = 4;
cc.setup_bytes(megabytes << 20);
@@ -55,7 +55,7 @@ BOOST_AUTO_TEST_CASE(test_cuckoocache_no_fakes)
template <typename Cache>
static double test_cache(size_t megabytes, double load)
{
- SeedInsecureRand(SeedRand::ZEROS);
+ SeedRandomForTest(SeedRand::ZEROS);
std::vector<uint256> hashes;
Cache set{};
size_t bytes = megabytes * (1 << 20);
@@ -126,7 +126,7 @@ template <typename Cache>
static void test_cache_erase(size_t megabytes)
{
double load = 1;
- SeedInsecureRand(SeedRand::ZEROS);
+ SeedRandomForTest(SeedRand::ZEROS);
std::vector<uint256> hashes;
Cache set{};
size_t bytes = megabytes * (1 << 20);
@@ -189,7 +189,7 @@ template <typename Cache>
static void test_cache_erase_parallel(size_t megabytes)
{
double load = 1;
- SeedInsecureRand(SeedRand::ZEROS);
+ SeedRandomForTest(SeedRand::ZEROS);
std::vector<uint256> hashes;
Cache set{};
size_t bytes = megabytes * (1 << 20);
@@ -293,7 +293,7 @@ static void test_cache_generations()
// iterations with non-deterministic values, so it isn't "overfit" to the
// specific entropy in FastRandomContext(true) and implementation of the
// cache.
- SeedInsecureRand(SeedRand::ZEROS);
+ SeedRandomForTest(SeedRand::ZEROS);
// block_activity models a chunk of network activity. n_insert elements are
// added to the cache. The first and last n/4 are stored for removal later
diff --git a/src/test/descriptor_tests.cpp b/src/test/descriptor_tests.cpp
index e6821dd321..01b13fa794 100644
--- a/src/test/descriptor_tests.cpp
+++ b/src/test/descriptor_tests.cpp
@@ -441,20 +441,20 @@ BOOST_FIXTURE_TEST_SUITE(descriptor_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(descriptor_test)
{
// Basic single-key compressed
- Check("combo(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "combo(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "combo(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE, {{"2103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bdac","76a9149a1c78a507689f6f54b847ad1cef1e614ee23f1e88ac","00149a1c78a507689f6f54b847ad1cef1e614ee23f1e","a91484ab21b1b2fd065d4504ff693d832434b6108d7b87"}}, std::nullopt, /*op_desc_id=*/uint256S("8ef71f7b6ac0918663f6706be469d6109f6922e21f484009d7ab49d77da36e8b"));
- Check("pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE, {{"2103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bdac"}}, std::nullopt, /*op_desc_id=*/uint256S("5fe175b43c58ac2cdde40521dc7d1dbc607f3dd795d00770206f4fdefb42229e"));
- Check("pkh([deadbeef/1/2'/3/4']L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "pkh([deadbeef/1/2'/3/4']03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "pkh([deadbeef/1/2h/3/4h]03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE, {{"76a9149a1c78a507689f6f54b847ad1cef1e614ee23f1e88ac"}}, OutputType::LEGACY, /*op_desc_id=*/uint256S("628130ae0530f2b24faf1ad2744a83568ac0ffac43e703e30c00d5f137869b84"), {{1,0x80000002UL,3,0x80000004UL}});
- Check("wpkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "wpkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "wpkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE, {{"00149a1c78a507689f6f54b847ad1cef1e614ee23f1e"}}, OutputType::BECH32, /*op_desc_id=*/uint256S("4a47b7f497721bf3fc48c69a5d22bc1f3617238649a8ba7cb96fbd92fec84a7e"));
- Check("sh(wpkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "sh(wpkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", "sh(wpkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", SIGNABLE, {{"a91484ab21b1b2fd065d4504ff693d832434b6108d7b87"}}, OutputType::P2SH_SEGWIT, /*op_desc_id=*/uint256S("a13112753066b5c59473a87c5771b1694a10531944a60e0ab2d7ad66ecb65bcd"));
- Check("tr(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE | XONLY_KEYS, {{"512077aab6e066f8a7419c5ab714c12c67d25007ed55a43cadcacb4d7a970a093f11"}}, OutputType::BECH32M, /*op_desc_id=*/uint256S("4290f3d017b270be53b91abc56d9d2f23a3ff361d5b1d39550ba011e6cae0da5"));
+ Check("combo(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "combo(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "combo(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE, {{"2103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bdac","76a9149a1c78a507689f6f54b847ad1cef1e614ee23f1e88ac","00149a1c78a507689f6f54b847ad1cef1e614ee23f1e","a91484ab21b1b2fd065d4504ff693d832434b6108d7b87"}}, std::nullopt, /*op_desc_id=*/uint256{"8ef71f7b6ac0918663f6706be469d6109f6922e21f484009d7ab49d77da36e8b"});
+ Check("pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE, {{"2103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bdac"}}, std::nullopt, /*op_desc_id=*/uint256{"5fe175b43c58ac2cdde40521dc7d1dbc607f3dd795d00770206f4fdefb42229e"});
+ Check("pkh([deadbeef/1/2'/3/4']L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "pkh([deadbeef/1/2'/3/4']03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "pkh([deadbeef/1/2h/3/4h]03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE, {{"76a9149a1c78a507689f6f54b847ad1cef1e614ee23f1e88ac"}}, OutputType::LEGACY, /*op_desc_id=*/uint256{"628130ae0530f2b24faf1ad2744a83568ac0ffac43e703e30c00d5f137869b84"}, {{1,0x80000002UL,3,0x80000004UL}});
+ Check("wpkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "wpkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "wpkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE, {{"00149a1c78a507689f6f54b847ad1cef1e614ee23f1e"}}, OutputType::BECH32, /*op_desc_id=*/uint256{"4a47b7f497721bf3fc48c69a5d22bc1f3617238649a8ba7cb96fbd92fec84a7e"});
+ Check("sh(wpkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "sh(wpkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", "sh(wpkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", SIGNABLE, {{"a91484ab21b1b2fd065d4504ff693d832434b6108d7b87"}}, OutputType::P2SH_SEGWIT, /*op_desc_id=*/uint256{"a13112753066b5c59473a87c5771b1694a10531944a60e0ab2d7ad66ecb65bcd"});
+ Check("tr(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE | XONLY_KEYS, {{"512077aab6e066f8a7419c5ab714c12c67d25007ed55a43cadcacb4d7a970a093f11"}}, OutputType::BECH32M, /*op_desc_id=*/uint256{"4290f3d017b270be53b91abc56d9d2f23a3ff361d5b1d39550ba011e6cae0da5"});
CheckUnparsable("sh(wpkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY2))", "sh(wpkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5))", "wpkh(): Pubkey '03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5' is invalid"); // Invalid pubkey
CheckUnparsable("pkh(deadbeef/1/2'/3/4']L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "pkh(deadbeef/1/2h/3/4h]03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "pkh(): Key origin start '[ character expected but not found, got 'd' instead"); // Missing start bracket in key origin
CheckUnparsable("pkh([deadbeef]/1/2'/3/4']L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "pkh([deadbeef]/1/2'/3/4']03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "pkh(): Multiple ']' characters found for a single pubkey"); // Multiple end brackets in key origin
// Basic single-key uncompressed
- Check("combo(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "combo(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "combo(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)",SIGNABLE, {{"4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235ac","76a914b5bd079c4d57cc7fc28ecf8213a6b791625b818388ac"}}, std::nullopt, /*op_desc_id=*/uint256S("33f6bb5d32c04e9d9e5466a8212836743bd5466aa0b8d5331ce8aa0812371ffd"));
- Check("pk(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "pk(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "pk(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", SIGNABLE, {{"4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235ac"}}, std::nullopt, /*op_desc_id=*/uint256S("52306fc1f5d0cb78aacea9d3933092be9252adc27b146f97c16a94d6fcdb652e"));
- Check("pkh(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "pkh(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "pkh(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", SIGNABLE, {{"76a914b5bd079c4d57cc7fc28ecf8213a6b791625b818388ac"}}, OutputType::LEGACY, /*op_desc_id=*/uint256S("36657e8690d4015032da1a8c1e37b315c3f7ccb010e6ada12967878711962991"));
+ Check("combo(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "combo(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "combo(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)",SIGNABLE, {{"4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235ac","76a914b5bd079c4d57cc7fc28ecf8213a6b791625b818388ac"}}, std::nullopt, /*op_desc_id=*/uint256{"33f6bb5d32c04e9d9e5466a8212836743bd5466aa0b8d5331ce8aa0812371ffd"});
+ Check("pk(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "pk(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "pk(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", SIGNABLE, {{"4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235ac"}}, std::nullopt, /*op_desc_id=*/uint256{"52306fc1f5d0cb78aacea9d3933092be9252adc27b146f97c16a94d6fcdb652e"});
+ Check("pkh(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "pkh(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "pkh(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", SIGNABLE, {{"76a914b5bd079c4d57cc7fc28ecf8213a6b791625b818388ac"}}, OutputType::LEGACY, /*op_desc_id=*/uint256{"36657e8690d4015032da1a8c1e37b315c3f7ccb010e6ada12967878711962991"});
CheckUnparsable("wpkh(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "wpkh(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "wpkh(): Uncompressed keys are not allowed"); // No uncompressed keys in witness
CheckUnparsable("wsh(pk(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss))", "wsh(pk(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235))", "pk(): Uncompressed keys are not allowed"); // No uncompressed keys in witness
CheckUnparsable("sh(wpkh(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss))", "sh(wpkh(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235))", "wpkh(): Uncompressed keys are not allowed"); // No uncompressed keys in witness
@@ -474,16 +474,16 @@ BOOST_AUTO_TEST_CASE(descriptor_test)
Check("tr(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5,{pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5),{pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1),pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5)}})", "tr(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5,{pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5),{pk(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd),pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5)}})", "tr(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5,{pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5),{pk(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd),pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5)}})", XONLY_KEYS | SIGNABLE | MISSING_PRIVKEYS, {{"51201497ae16f30dacb88523ed9301bff17773b609e8a90518a3f96ea328a47d1500"}}, OutputType::BECH32M);
// Versions with BIP32 derivations
- Check("combo([01234567]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc)", "combo([01234567]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)", "combo([01234567]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)", SIGNABLE, {{"2102d2b36900396c9282fa14628566582f206a5dd0bcc8d5e892611806cafb0301f0ac","76a91431a507b815593dfc51ffc7245ae7e5aee304246e88ac","001431a507b815593dfc51ffc7245ae7e5aee304246e","a9142aafb926eb247cb18240a7f4c07983ad1f37922687"}}, std::nullopt, /*op_desc_id=*/uint256S("7f127f7861594e3ede449eb47a7cc623c753acc0b0f0fc03fbb2dac636c20d6f"));
- Check("pk(xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0)", "pk(xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0)", "pk(xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0)", DEFAULT, {{"210379e45b3cf75f9c5f9befd8e9506fb962f6a9d185ac87001ec44a8d3df8d4a9e3ac"}}, std::nullopt, /*op_desc_id=*/uint256S("0e54cf04f2bb8d607e2241d611d169c6f7d78f0ab1f15a80642192a19fbdb7cc"), {{0}});
- Check("pkh(xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0)", "pkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647'/0)", "pkh([bd16bee5/2147483647h]xpub69H7F5dQzmVd3vPuLKtcXJziMEQByuDidnX3YdwgtNsecY5HRGtAAQC5mXTt4dsv9RzyjgDjAQs9VGVV6ydYCHnprc9vvaA5YtqWyL6hyds/0)", HARDENED, {{"76a914ebdc90806a9c4356c1c88e42216611e1cb4c1c1788ac"}}, OutputType::LEGACY, /*op_desc_id=*/uint256S("35a5cf511e941a35b9cb0cf515d3ef887aa4246db87d6af463265a386ad856fe"), {{0xFFFFFFFFUL,0}});
+ Check("combo([01234567]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc)", "combo([01234567]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)", "combo([01234567]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)", SIGNABLE, {{"2102d2b36900396c9282fa14628566582f206a5dd0bcc8d5e892611806cafb0301f0ac","76a91431a507b815593dfc51ffc7245ae7e5aee304246e88ac","001431a507b815593dfc51ffc7245ae7e5aee304246e","a9142aafb926eb247cb18240a7f4c07983ad1f37922687"}}, std::nullopt, /*op_desc_id=*/uint256{"7f127f7861594e3ede449eb47a7cc623c753acc0b0f0fc03fbb2dac636c20d6f"});
+ Check("pk(xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0)", "pk(xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0)", "pk(xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0)", DEFAULT, {{"210379e45b3cf75f9c5f9befd8e9506fb962f6a9d185ac87001ec44a8d3df8d4a9e3ac"}}, std::nullopt, /*op_desc_id=*/uint256{"0e54cf04f2bb8d607e2241d611d169c6f7d78f0ab1f15a80642192a19fbdb7cc"}, {{0}});
+ Check("pkh(xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0)", "pkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647'/0)", "pkh([bd16bee5/2147483647h]xpub69H7F5dQzmVd3vPuLKtcXJziMEQByuDidnX3YdwgtNsecY5HRGtAAQC5mXTt4dsv9RzyjgDjAQs9VGVV6ydYCHnprc9vvaA5YtqWyL6hyds/0)", HARDENED, {{"76a914ebdc90806a9c4356c1c88e42216611e1cb4c1c1788ac"}}, OutputType::LEGACY, /*op_desc_id=*/uint256{"35a5cf511e941a35b9cb0cf515d3ef887aa4246db87d6af463265a386ad856fe"}, {{0xFFFFFFFFUL,0}});
Check("wpkh([ffffffff/13']xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*)", "wpkh([ffffffff/13']xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*)", "wpkh([ffffffff/13h]xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*)", RANGE, {{"0014326b2249e3a25d5dc60935f044ee835d090ba859"},{"0014af0bd98abc2f2cae66e36896a39ffe2d32984fb7"},{"00141fa798efd1cbf95cebf912c031b8a4a6e9fb9f27"}}, OutputType::BECH32, /*op_desc_id=*/std::nullopt, {{0x8000000DUL, 1, 2, 0}, {0x8000000DUL, 1, 2, 1}, {0x8000000DUL, 1, 2, 2}});
Check("sh(wpkh(xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "sh(wpkh(xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))", "sh(wpkh(xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*h))", RANGE | HARDENED | DERIVE_HARDENED, {{"a9149a4d9901d6af519b2a23d4a2f51650fcba87ce7b87"},{"a914bed59fc0024fae941d6e20a3b44a109ae740129287"},{"a9148483aa1116eb9c05c482a72bada4b1db24af654387"}}, OutputType::P2SH_SEGWIT, /*op_desc_id=*/std::nullopt, {{10, 20, 30, 40, 0x80000000UL}, {10, 20, 30, 40, 0x80000001UL}, {10, 20, 30, 40, 0x80000002UL}});
Check("combo(xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334/*)", "combo(xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV/*)", "combo(xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV/*)", RANGE, {{"2102df12b7035bdac8e3bab862a3a83d06ea6b17b6753d52edecba9be46f5d09e076ac","76a914f90e3178ca25f2c808dc76624032d352fdbdfaf288ac","0014f90e3178ca25f2c808dc76624032d352fdbdfaf2","a91408f3ea8c68d4a7585bf9e8bda226723f70e445f087"},{"21032869a233c9adff9a994e4966e5b821fd5bac066da6c3112488dc52383b4a98ecac","76a914a8409d1b6dfb1ed2a3e8aa5e0ef2ff26b15b75b788ac","0014a8409d1b6dfb1ed2a3e8aa5e0ef2ff26b15b75b7","a91473e39884cb71ae4e5ac9739e9225026c99763e6687"}}, std::nullopt, /*op_desc_id=*/std::nullopt, {{0}, {1}});
Check("tr(xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc/0/*,pk(xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc/1/*))", "tr(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/0/*,pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/1/*))", "tr(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/0/*,pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/1/*))", XONLY_KEYS | RANGE, {{"512078bc707124daa551b65af74de2ec128b7525e10f374dc67b64e00ce0ab8b3e12"}, {"512001f0a02a17808c20134b78faab80ef93ffba82261ccef0a2314f5d62b6438f11"}, {"512021024954fcec88237a9386fce80ef2ced5f1e91b422b26c59ccfc174c8d1ad25"}}, OutputType::BECH32M, /*op_desc_id=*/std::nullopt, {{0, 0}, {0, 1}, {0, 2}, {1, 0}, {1, 1}, {1, 2}});
// Mixed xpubs and const pubkeys
- Check("wsh(multi(1,xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334/0,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))","wsh(multi(1,xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV/0,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))","wsh(multi(1,xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV/0,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", MIXED_PUBKEYS, {{"0020cb155486048b23a6da976d4c6fe071a2dbc8a7b57aaf225b8955f2e2a27b5f00"}},OutputType::BECH32, /*op_desc_id=*/uint256S("88af8e5951779aa054dfe1071ef0f7266ba1c5558487bfd8525c95010fc0aba2"),{{0},{}});
+ Check("wsh(multi(1,xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334/0,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))","wsh(multi(1,xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV/0,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))","wsh(multi(1,xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV/0,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", MIXED_PUBKEYS, {{"0020cb155486048b23a6da976d4c6fe071a2dbc8a7b57aaf225b8955f2e2a27b5f00"}},OutputType::BECH32, /*op_desc_id=*/uint256{"88af8e5951779aa054dfe1071ef0f7266ba1c5558487bfd8525c95010fc0aba2"},{{0},{}});
// Mixed range xpubs and const pubkeys
Check("multi(1,xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334/*,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)","multi(1,xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV/*,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)","multi(1,xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV/*,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", RANGE | MIXED_PUBKEYS, {{"512102df12b7035bdac8e3bab862a3a83d06ea6b17b6753d52edecba9be46f5d09e0762103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd52ae"},{"5121032869a233c9adff9a994e4966e5b821fd5bac066da6c3112488dc52383b4a98ec2103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd52ae"},{"5121035d30b6c66dc1e036c45369da8287518cf7e0d6ed1e2b905171c605708f14ca032103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd52ae"}}, std::nullopt, /*op_desc_id=*/std::nullopt,{{2},{1},{0},{}});
@@ -493,14 +493,14 @@ BOOST_AUTO_TEST_CASE(descriptor_test)
Check("pkh([01234567/10/20]xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0)", "pkh([01234567/10/20]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647'/0)", "pkh([01234567/10/20/2147483647h]xpub69H7F5dQzmVd3vPuLKtcXJziMEQByuDidnX3YdwgtNsecY5HRGtAAQC5mXTt4dsv9RzyjgDjAQs9VGVV6ydYCHnprc9vvaA5YtqWyL6hyds/0)", HARDENED, {{"76a914ebdc90806a9c4356c1c88e42216611e1cb4c1c1788ac"}}, OutputType::LEGACY, /*op_desc_id=*/std::nullopt, {{10, 20, 0xFFFFFFFFUL, 0}});
// Multisig constructions
- Check("multi(1,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "multi(1,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "multi(1,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", SIGNABLE, {{"512103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea23552ae"}}, std::nullopt, /*op_desc_id=*/uint256S("b147e25eb4a9d3da4e86ed8e970d817563ae2cb9c71a756b11cfdeb4dc11b70c"));
- Check("sortedmulti(1,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "sortedmulti(1,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "sortedmulti(1,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", SIGNABLE, {{"512103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea23552ae"}}, std::nullopt, /*op_desc_id=*/uint256S("62b59d1e32a62176ef7a17538f3b80c7d1afc53e5644eb753525bdb5d556486c"));
+ Check("multi(1,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "multi(1,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "multi(1,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", SIGNABLE, {{"512103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea23552ae"}}, std::nullopt, /*op_desc_id=*/uint256{"b147e25eb4a9d3da4e86ed8e970d817563ae2cb9c71a756b11cfdeb4dc11b70c"});
+ Check("sortedmulti(1,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "sortedmulti(1,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "sortedmulti(1,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", SIGNABLE, {{"512103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea23552ae"}}, std::nullopt, /*op_desc_id=*/uint256{"62b59d1e32a62176ef7a17538f3b80c7d1afc53e5644eb753525bdb5d556486c"});
Check("sortedmulti(1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "sortedmulti(1,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "sortedmulti(1,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE, {{"512103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea23552ae"}}, std::nullopt);
Check("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))", "sh(multi(2,[00000000/111h/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))", DEFAULT, {{"a91445a9a622a8b0a1269944be477640eedc447bbd8487"}}, OutputType::LEGACY, /*op_desc_id=*/std::nullopt, {{0x8000006FUL,222},{0}});
Check("sortedmulti(2,xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc/*,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0/0/*)", "sortedmulti(2,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/*,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0/0/*)", "sortedmulti(2,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/*,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0/0/*)", RANGE, {{"5221025d5fc65ebb8d44a5274b53bac21ff8307fec2334a32df05553459f8b1f7fe1b62102fbd47cc8034098f0e6a94c6aeee8528abf0a2153a5d8e46d325b7284c046784652ae"}, {"52210264fd4d1f5dea8ded94c61e9641309349b62f27fbffe807291f664e286bfbe6472103f4ece6dfccfa37b211eb3d0af4d0c61dba9ef698622dc17eecdf764beeb005a652ae"}, {"5221022ccabda84c30bad578b13c89eb3b9544ce149787e5b538175b1d1ba259cbb83321024d902e1a2fc7a8755ab5b694c575fce742c48d9ff192e63df5193e4c7afe1f9c52ae"}}, std::nullopt, /*op_desc_id=*/std::nullopt, {{0}, {1}, {2}, {0, 0, 0}, {0, 0, 1}, {0, 0, 2}});
Check("wsh(multi(2,xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0,xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "wsh(multi(2,xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647'/0,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))", "wsh(multi(2,[bd16bee5/2147483647h]xpub69H7F5dQzmVd3vPuLKtcXJziMEQByuDidnX3YdwgtNsecY5HRGtAAQC5mXTt4dsv9RzyjgDjAQs9VGVV6ydYCHnprc9vvaA5YtqWyL6hyds/0,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*h))", HARDENED | RANGE | DERIVE_HARDENED, {{"0020b92623201f3bb7c3771d45b2ad1d0351ea8fbf8cfe0a0e570264e1075fa1948f"},{"002036a08bbe4923af41cf4316817c93b8d37e2f635dd25cfff06bd50df6ae7ea203"},{"0020a96e7ab4607ca6b261bfe3245ffda9c746b28d3f59e83d34820ec0e2b36c139c"}}, OutputType::BECH32, /*op_desc_id=*/std::nullopt, {{0xFFFFFFFFUL,0}, {1,2,0}, {1,2,1}, {1,2,2}, {10, 20, 30, 40, 0x80000000UL}, {10, 20, 30, 40, 0x80000001UL}, {10, 20, 30, 40, 0x80000002UL}});
Check("sh(wsh(multi(16,KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy,KwGNz6YCCQtYvFzMtrC6D3tKTKdBBboMrLTsjr2NYVBwapCkn7Mr,KxogYhiNfwxuswvXV66eFyKcCpm7dZ7TqHVqujHAVUjJxyivxQ9X,L2BUNduTSyZwZjwNHynQTF14mv2uz2NRq5n5sYWTb4FkkmqgEE9f,L1okJGHGn1kFjdXHKxXjwVVtmCMR2JA5QsbKCSpSb7ReQjezKeoD,KxDCNSST75HFPaW5QKpzHtAyaCQC7p9Vo3FYfi2u4dXD1vgMiboK,L5edQjFtnkcf5UWURn6UuuoFrabgDQUHdheKCziwN42aLwS3KizU,KzF8UWFcEC7BYTq8Go1xVimMkDmyNYVmXV5PV7RuDicvAocoPB8i,L3nHUboKG2w4VSJ5jYZ5CBM97oeK6YuKvfZxrefdShECcjEYKMWZ,KyjHo36dWkYhimKmVVmQTq3gERv3pnqA4xFCpvUgbGDJad7eS8WE,KwsfyHKRUTZPQtysN7M3tZ4GXTnuov5XRgjdF2XCG8faAPmFruRF,KzCUbGhN9LJhdeFfL9zQgTJMjqxdBKEekRGZX24hXdgCNCijkkap,KzgpMBwwsDLwkaC5UrmBgCYaBD2WgZ7PBoGYXR8KT7gCA9UTN5a3,KyBXTPy4T7YG4q9tcAM3LkvfRpD1ybHMvcJ2ehaWXaSqeGUxEdkP,KzJDe9iwJRPtKP2F2AoN6zBgzS7uiuAwhWCfGdNeYJ3PC1HNJ8M8,L1xbHrxynrqLKkoYc4qtoQPx6uy5qYXR5ZDYVYBSRmCV5piU3JG9)))","sh(wsh(multi(16,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8,02da72e8b46901a65d4374fe6315538d8f368557dda3a1dcf9ea903f3afe7314c8,0318c82dd0b53fd3a932d16e0ba9e278fcc937c582d5781be626ff16e201f72286,0297ccef1ef99f9d73dec9ad37476ddb232f1238aff877af19e72ba04493361009,02e502cfd5c3f972fe9a3e2a18827820638f96b6f347e54d63deb839011fd5765d,03e687710f0e3ebe81c1037074da939d409c0025f17eb86adb9427d28f0f7ae0e9,02c04d3a5274952acdbc76987f3184b346a483d43be40874624b29e3692c1df5af,02ed06e0f418b5b43a7ec01d1d7d27290fa15f75771cb69b642a51471c29c84acd,036d46073cbb9ffee90473f3da429abc8de7f8751199da44485682a989a4bebb24,02f5d1ff7c9029a80a4e36b9a5497027ef7f3e73384a4a94fbfe7c4e9164eec8bc,02e41deffd1b7cce11cde209a781adcffdabd1b91c0ba0375857a2bfd9302419f3,02d76625f7956a7fc505ab02556c23ee72d832f1bac391bcd2d3abce5710a13d06,0399eb0a5487515802dc14544cf10b3666623762fbed2ec38a3975716e2c29c232)))", "sh(wsh(multi(16,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8,02da72e8b46901a65d4374fe6315538d8f368557dda3a1dcf9ea903f3afe7314c8,0318c82dd0b53fd3a932d16e0ba9e278fcc937c582d5781be626ff16e201f72286,0297ccef1ef99f9d73dec9ad37476ddb232f1238aff877af19e72ba04493361009,02e502cfd5c3f972fe9a3e2a18827820638f96b6f347e54d63deb839011fd5765d,03e687710f0e3ebe81c1037074da939d409c0025f17eb86adb9427d28f0f7ae0e9,02c04d3a5274952acdbc76987f3184b346a483d43be40874624b29e3692c1df5af,02ed06e0f418b5b43a7ec01d1d7d27290fa15f75771cb69b642a51471c29c84acd,036d46073cbb9ffee90473f3da429abc8de7f8751199da44485682a989a4bebb24,02f5d1ff7c9029a80a4e36b9a5497027ef7f3e73384a4a94fbfe7c4e9164eec8bc,02e41deffd1b7cce11cde209a781adcffdabd1b91c0ba0375857a2bfd9302419f3,02d76625f7956a7fc505ab02556c23ee72d832f1bac391bcd2d3abce5710a13d06,0399eb0a5487515802dc14544cf10b3666623762fbed2ec38a3975716e2c29c232)))", SIGNABLE, {{"a9147fc63e13dc25e8a95a3cee3d9a714ac3afd96f1e87"}}, OutputType::P2SH_SEGWIT, /*op_desc_id=*/std::nullopt);
- Check("tr(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,pk(KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy))", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,pk(669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0))", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,pk(669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0))", SIGNABLE | XONLY_KEYS, {{"512017cf18db381d836d8923b1bdb246cfcd818da1a9f0e6e7907f187f0b2f937754"}}, OutputType::BECH32M, /*op_desc_id=*/uint256S("af482b44c10b737b678e1091584818372e169e2dc5219e2877fabe1b83ae467b"));
+ Check("tr(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,pk(KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy))", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,pk(669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0))", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,pk(669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0))", SIGNABLE | XONLY_KEYS, {{"512017cf18db381d836d8923b1bdb246cfcd818da1a9f0e6e7907f187f0b2f937754"}}, OutputType::BECH32M, /*op_desc_id=*/uint256{"af482b44c10b737b678e1091584818372e169e2dc5219e2877fabe1b83ae467b"});
Check("tr(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,multi_a(1,KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy))", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,multi_a(1,669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0))", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,multi_a(1,669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0))", SIGNABLE | XONLY_KEYS, {{"5120eb5bd3894327d75093891cc3a62506df7d58ec137fcd104cdd285d67816074f3"}}, OutputType::BECH32M);
CheckUnparsable("sh(multi(16,KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy,KwGNz6YCCQtYvFzMtrC6D3tKTKdBBboMrLTsjr2NYVBwapCkn7Mr,KxogYhiNfwxuswvXV66eFyKcCpm7dZ7TqHVqujHAVUjJxyivxQ9X,L2BUNduTSyZwZjwNHynQTF14mv2uz2NRq5n5sYWTb4FkkmqgEE9f,L1okJGHGn1kFjdXHKxXjwVVtmCMR2JA5QsbKCSpSb7ReQjezKeoD,KxDCNSST75HFPaW5QKpzHtAyaCQC7p9Vo3FYfi2u4dXD1vgMiboK,L5edQjFtnkcf5UWURn6UuuoFrabgDQUHdheKCziwN42aLwS3KizU,KzF8UWFcEC7BYTq8Go1xVimMkDmyNYVmXV5PV7RuDicvAocoPB8i,L3nHUboKG2w4VSJ5jYZ5CBM97oeK6YuKvfZxrefdShECcjEYKMWZ,KyjHo36dWkYhimKmVVmQTq3gERv3pnqA4xFCpvUgbGDJad7eS8WE,KwsfyHKRUTZPQtysN7M3tZ4GXTnuov5XRgjdF2XCG8faAPmFruRF,KzCUbGhN9LJhdeFfL9zQgTJMjqxdBKEekRGZX24hXdgCNCijkkap,KzgpMBwwsDLwkaC5UrmBgCYaBD2WgZ7PBoGYXR8KT7gCA9UTN5a3,KyBXTPy4T7YG4q9tcAM3LkvfRpD1ybHMvcJ2ehaWXaSqeGUxEdkP,KzJDe9iwJRPtKP2F2AoN6zBgzS7uiuAwhWCfGdNeYJ3PC1HNJ8M8,L1xbHrxynrqLKkoYc4qtoQPx6uy5qYXR5ZDYVYBSRmCV5piU3JG9))","sh(multi(16,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8,02da72e8b46901a65d4374fe6315538d8f368557dda3a1dcf9ea903f3afe7314c8,0318c82dd0b53fd3a932d16e0ba9e278fcc937c582d5781be626ff16e201f72286,0297ccef1ef99f9d73dec9ad37476ddb232f1238aff877af19e72ba04493361009,02e502cfd5c3f972fe9a3e2a18827820638f96b6f347e54d63deb839011fd5765d,03e687710f0e3ebe81c1037074da939d409c0025f17eb86adb9427d28f0f7ae0e9,02c04d3a5274952acdbc76987f3184b346a483d43be40874624b29e3692c1df5af,02ed06e0f418b5b43a7ec01d1d7d27290fa15f75771cb69b642a51471c29c84acd,036d46073cbb9ffee90473f3da429abc8de7f8751199da44485682a989a4bebb24,02f5d1ff7c9029a80a4e36b9a5497027ef7f3e73384a4a94fbfe7c4e9164eec8bc,02e41deffd1b7cce11cde209a781adcffdabd1b91c0ba0375857a2bfd9302419f3,02d76625f7956a7fc505ab02556c23ee72d832f1bac391bcd2d3abce5710a13d06,0399eb0a5487515802dc14544cf10b3666623762fbed2ec38a3975716e2c29c232))", "P2SH script is too large, 547 bytes is larger than 520 bytes"); // P2SH does not fit 16 compressed pubkeys in a redeemscript
CheckUnparsable("wsh(multi(2,[aaaaaaaa][aaaaaaaa]xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0,xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "wsh(multi(2,[aaaaaaaa][aaaaaaaa]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647h/0,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*h))", "Multi: Multiple ']' characters found for a single pubkey"); // Double key origin descriptor
@@ -513,8 +513,8 @@ BOOST_AUTO_TEST_CASE(descriptor_test)
CheckUnparsable("multi(3,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "multi(3,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "Multisig threshold cannot be larger than the number of keys; threshold is 3 but only 2 keys specified"); // Threshold larger than number of keys
CheckUnparsable("multi(3,KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy,KwGNz6YCCQtYvFzMtrC6D3tKTKdBBboMrLTsjr2NYVBwapCkn7Mr,KxogYhiNfwxuswvXV66eFyKcCpm7dZ7TqHVqujHAVUjJxyivxQ9X,L2BUNduTSyZwZjwNHynQTF14mv2uz2NRq5n5sYWTb4FkkmqgEE9f)", "multi(3,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8)", "Cannot have 4 pubkeys in bare multisig; only at most 3 pubkeys"); // Threshold larger than number of keys
CheckUnparsable("sh(multi(16,KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy,KwGNz6YCCQtYvFzMtrC6D3tKTKdBBboMrLTsjr2NYVBwapCkn7Mr,KxogYhiNfwxuswvXV66eFyKcCpm7dZ7TqHVqujHAVUjJxyivxQ9X,L2BUNduTSyZwZjwNHynQTF14mv2uz2NRq5n5sYWTb4FkkmqgEE9f,L1okJGHGn1kFjdXHKxXjwVVtmCMR2JA5QsbKCSpSb7ReQjezKeoD,KxDCNSST75HFPaW5QKpzHtAyaCQC7p9Vo3FYfi2u4dXD1vgMiboK,L5edQjFtnkcf5UWURn6UuuoFrabgDQUHdheKCziwN42aLwS3KizU,KzF8UWFcEC7BYTq8Go1xVimMkDmyNYVmXV5PV7RuDicvAocoPB8i,L3nHUboKG2w4VSJ5jYZ5CBM97oeK6YuKvfZxrefdShECcjEYKMWZ,KyjHo36dWkYhimKmVVmQTq3gERv3pnqA4xFCpvUgbGDJad7eS8WE,KwsfyHKRUTZPQtysN7M3tZ4GXTnuov5XRgjdF2XCG8faAPmFruRF,KzCUbGhN9LJhdeFfL9zQgTJMjqxdBKEekRGZX24hXdgCNCijkkap,KzgpMBwwsDLwkaC5UrmBgCYaBD2WgZ7PBoGYXR8KT7gCA9UTN5a3,KyBXTPy4T7YG4q9tcAM3LkvfRpD1ybHMvcJ2ehaWXaSqeGUxEdkP,KzJDe9iwJRPtKP2F2AoN6zBgzS7uiuAwhWCfGdNeYJ3PC1HNJ8M8,L1xbHrxynrqLKkoYc4qtoQPx6uy5qYXR5ZDYVYBSRmCV5piU3JG9,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))","sh(multi(16,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8,02da72e8b46901a65d4374fe6315538d8f368557dda3a1dcf9ea903f3afe7314c8,0318c82dd0b53fd3a932d16e0ba9e278fcc937c582d5781be626ff16e201f72286,0297ccef1ef99f9d73dec9ad37476ddb232f1238aff877af19e72ba04493361009,02e502cfd5c3f972fe9a3e2a18827820638f96b6f347e54d63deb839011fd5765d,03e687710f0e3ebe81c1037074da939d409c0025f17eb86adb9427d28f0f7ae0e9,02c04d3a5274952acdbc76987f3184b346a483d43be40874624b29e3692c1df5af,02ed06e0f418b5b43a7ec01d1d7d27290fa15f75771cb69b642a51471c29c84acd,036d46073cbb9ffee90473f3da429abc8de7f8751199da44485682a989a4bebb24,02f5d1ff7c9029a80a4e36b9a5497027ef7f3e73384a4a94fbfe7c4e9164eec8bc,02e41deffd1b7cce11cde209a781adcffdabd1b91c0ba0375857a2bfd9302419f3,02d76625f7956a7fc505ab02556c23ee72d832f1bac391bcd2d3abce5710a13d06,0399eb0a5487515802dc14544cf10b3666623762fbed2ec38a3975716e2c29c232,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", "P2SH script is too large, 581 bytes is larger than 520 bytes"); // Cannot have more than 15 keys in a P2SH multisig, or we exceed maximum push size
- Check("wsh(multi(20,KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy,KwGNz6YCCQtYvFzMtrC6D3tKTKdBBboMrLTsjr2NYVBwapCkn7Mr,KxogYhiNfwxuswvXV66eFyKcCpm7dZ7TqHVqujHAVUjJxyivxQ9X,L2BUNduTSyZwZjwNHynQTF14mv2uz2NRq5n5sYWTb4FkkmqgEE9f,L1okJGHGn1kFjdXHKxXjwVVtmCMR2JA5QsbKCSpSb7ReQjezKeoD,KxDCNSST75HFPaW5QKpzHtAyaCQC7p9Vo3FYfi2u4dXD1vgMiboK,L5edQjFtnkcf5UWURn6UuuoFrabgDQUHdheKCziwN42aLwS3KizU,KzF8UWFcEC7BYTq8Go1xVimMkDmyNYVmXV5PV7RuDicvAocoPB8i,L3nHUboKG2w4VSJ5jYZ5CBM97oeK6YuKvfZxrefdShECcjEYKMWZ,KyjHo36dWkYhimKmVVmQTq3gERv3pnqA4xFCpvUgbGDJad7eS8WE,KwsfyHKRUTZPQtysN7M3tZ4GXTnuov5XRgjdF2XCG8faAPmFruRF,KzCUbGhN9LJhdeFfL9zQgTJMjqxdBKEekRGZX24hXdgCNCijkkap,KzgpMBwwsDLwkaC5UrmBgCYaBD2WgZ7PBoGYXR8KT7gCA9UTN5a3,KyBXTPy4T7YG4q9tcAM3LkvfRpD1ybHMvcJ2ehaWXaSqeGUxEdkP,KzJDe9iwJRPtKP2F2AoN6zBgzS7uiuAwhWCfGdNeYJ3PC1HNJ8M8,L1xbHrxynrqLKkoYc4qtoQPx6uy5qYXR5ZDYVYBSRmCV5piU3JG9,KzRedjSwMggebB3VufhbzpYJnvHfHe9kPJSjCU5QpJdAW3NSZxYS,Kyjtp5858xL7JfeV4PNRCKy2t6XvgqNNepArGY9F9F1SSPqNEMs3,L2D4RLHPiHBidkHS8ftx11jJk1hGFELvxh8LoxNQheaGT58dKenW,KyLPZdwY4td98bKkXqEXTEBX3vwEYTQo1yyLjX2jKXA63GBpmSjv))","wsh(multi(20,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8,02da72e8b46901a65d4374fe6315538d8f368557dda3a1dcf9ea903f3afe7314c8,0318c82dd0b53fd3a932d16e0ba9e278fcc937c582d5781be626ff16e201f72286,0297ccef1ef99f9d73dec9ad37476ddb232f1238aff877af19e72ba04493361009,02e502cfd5c3f972fe9a3e2a18827820638f96b6f347e54d63deb839011fd5765d,03e687710f0e3ebe81c1037074da939d409c0025f17eb86adb9427d28f0f7ae0e9,02c04d3a5274952acdbc76987f3184b346a483d43be40874624b29e3692c1df5af,02ed06e0f418b5b43a7ec01d1d7d27290fa15f75771cb69b642a51471c29c84acd,036d46073cbb9ffee90473f3da429abc8de7f8751199da44485682a989a4bebb24,02f5d1ff7c9029a80a4e36b9a5497027ef7f3e73384a4a94fbfe7c4e9164eec8bc,02e41deffd1b7cce11cde209a781adcffdabd1b91c0ba0375857a2bfd9302419f3,02d76625f7956a7fc505ab02556c23ee72d832f1bac391bcd2d3abce5710a13d06,0399eb0a5487515802dc14544cf10b3666623762fbed2ec38a3975716e2c29c232,02bc2feaa536991d269aae46abb8f3772a5b3ad592314945e51543e7da84c4af6e,0318bf32e5217c1eb771a6d5ce1cd39395dff7ff665704f175c9a5451d95a2f2ca,02c681a6243f16208c2004bb81f5a8a67edfdd3e3711534eadeec3dcf0b010c759,0249fdd6b69768b8d84b4893f8ff84b36835c50183de20fcae8f366a45290d01fd))", "wsh(multi(20,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8,02da72e8b46901a65d4374fe6315538d8f368557dda3a1dcf9ea903f3afe7314c8,0318c82dd0b53fd3a932d16e0ba9e278fcc937c582d5781be626ff16e201f72286,0297ccef1ef99f9d73dec9ad37476ddb232f1238aff877af19e72ba04493361009,02e502cfd5c3f972fe9a3e2a18827820638f96b6f347e54d63deb839011fd5765d,03e687710f0e3ebe81c1037074da939d409c0025f17eb86adb9427d28f0f7ae0e9,02c04d3a5274952acdbc76987f3184b346a483d43be40874624b29e3692c1df5af,02ed06e0f418b5b43a7ec01d1d7d27290fa15f75771cb69b642a51471c29c84acd,036d46073cbb9ffee90473f3da429abc8de7f8751199da44485682a989a4bebb24,02f5d1ff7c9029a80a4e36b9a5497027ef7f3e73384a4a94fbfe7c4e9164eec8bc,02e41deffd1b7cce11cde209a781adcffdabd1b91c0ba0375857a2bfd9302419f3,02d76625f7956a7fc505ab02556c23ee72d832f1bac391bcd2d3abce5710a13d06,0399eb0a5487515802dc14544cf10b3666623762fbed2ec38a3975716e2c29c232,02bc2feaa536991d269aae46abb8f3772a5b3ad592314945e51543e7da84c4af6e,0318bf32e5217c1eb771a6d5ce1cd39395dff7ff665704f175c9a5451d95a2f2ca,02c681a6243f16208c2004bb81f5a8a67edfdd3e3711534eadeec3dcf0b010c759,0249fdd6b69768b8d84b4893f8ff84b36835c50183de20fcae8f366a45290d01fd))", SIGNABLE, {{"0020376bd8344b8b6ebe504ff85ef743eaa1aa9272178223bcb6887e9378efb341ac"}}, OutputType::BECH32, /*op_desc_id=*/uint256S("2bb9d418ebdc3a75c465383985881527f3e5d6e520fb3efb152d4191b80e8412")); // In P2WSH we can have up to 20 keys
- Check("sh(wsh(multi(20,KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy,KwGNz6YCCQtYvFzMtrC6D3tKTKdBBboMrLTsjr2NYVBwapCkn7Mr,KxogYhiNfwxuswvXV66eFyKcCpm7dZ7TqHVqujHAVUjJxyivxQ9X,L2BUNduTSyZwZjwNHynQTF14mv2uz2NRq5n5sYWTb4FkkmqgEE9f,L1okJGHGn1kFjdXHKxXjwVVtmCMR2JA5QsbKCSpSb7ReQjezKeoD,KxDCNSST75HFPaW5QKpzHtAyaCQC7p9Vo3FYfi2u4dXD1vgMiboK,L5edQjFtnkcf5UWURn6UuuoFrabgDQUHdheKCziwN42aLwS3KizU,KzF8UWFcEC7BYTq8Go1xVimMkDmyNYVmXV5PV7RuDicvAocoPB8i,L3nHUboKG2w4VSJ5jYZ5CBM97oeK6YuKvfZxrefdShECcjEYKMWZ,KyjHo36dWkYhimKmVVmQTq3gERv3pnqA4xFCpvUgbGDJad7eS8WE,KwsfyHKRUTZPQtysN7M3tZ4GXTnuov5XRgjdF2XCG8faAPmFruRF,KzCUbGhN9LJhdeFfL9zQgTJMjqxdBKEekRGZX24hXdgCNCijkkap,KzgpMBwwsDLwkaC5UrmBgCYaBD2WgZ7PBoGYXR8KT7gCA9UTN5a3,KyBXTPy4T7YG4q9tcAM3LkvfRpD1ybHMvcJ2ehaWXaSqeGUxEdkP,KzJDe9iwJRPtKP2F2AoN6zBgzS7uiuAwhWCfGdNeYJ3PC1HNJ8M8,L1xbHrxynrqLKkoYc4qtoQPx6uy5qYXR5ZDYVYBSRmCV5piU3JG9,KzRedjSwMggebB3VufhbzpYJnvHfHe9kPJSjCU5QpJdAW3NSZxYS,Kyjtp5858xL7JfeV4PNRCKy2t6XvgqNNepArGY9F9F1SSPqNEMs3,L2D4RLHPiHBidkHS8ftx11jJk1hGFELvxh8LoxNQheaGT58dKenW,KyLPZdwY4td98bKkXqEXTEBX3vwEYTQo1yyLjX2jKXA63GBpmSjv)))","sh(wsh(multi(20,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8,02da72e8b46901a65d4374fe6315538d8f368557dda3a1dcf9ea903f3afe7314c8,0318c82dd0b53fd3a932d16e0ba9e278fcc937c582d5781be626ff16e201f72286,0297ccef1ef99f9d73dec9ad37476ddb232f1238aff877af19e72ba04493361009,02e502cfd5c3f972fe9a3e2a18827820638f96b6f347e54d63deb839011fd5765d,03e687710f0e3ebe81c1037074da939d409c0025f17eb86adb9427d28f0f7ae0e9,02c04d3a5274952acdbc76987f3184b346a483d43be40874624b29e3692c1df5af,02ed06e0f418b5b43a7ec01d1d7d27290fa15f75771cb69b642a51471c29c84acd,036d46073cbb9ffee90473f3da429abc8de7f8751199da44485682a989a4bebb24,02f5d1ff7c9029a80a4e36b9a5497027ef7f3e73384a4a94fbfe7c4e9164eec8bc,02e41deffd1b7cce11cde209a781adcffdabd1b91c0ba0375857a2bfd9302419f3,02d76625f7956a7fc505ab02556c23ee72d832f1bac391bcd2d3abce5710a13d06,0399eb0a5487515802dc14544cf10b3666623762fbed2ec38a3975716e2c29c232,02bc2feaa536991d269aae46abb8f3772a5b3ad592314945e51543e7da84c4af6e,0318bf32e5217c1eb771a6d5ce1cd39395dff7ff665704f175c9a5451d95a2f2ca,02c681a6243f16208c2004bb81f5a8a67edfdd3e3711534eadeec3dcf0b010c759,0249fdd6b69768b8d84b4893f8ff84b36835c50183de20fcae8f366a45290d01fd)))", "sh(wsh(multi(20,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8,02da72e8b46901a65d4374fe6315538d8f368557dda3a1dcf9ea903f3afe7314c8,0318c82dd0b53fd3a932d16e0ba9e278fcc937c582d5781be626ff16e201f72286,0297ccef1ef99f9d73dec9ad37476ddb232f1238aff877af19e72ba04493361009,02e502cfd5c3f972fe9a3e2a18827820638f96b6f347e54d63deb839011fd5765d,03e687710f0e3ebe81c1037074da939d409c0025f17eb86adb9427d28f0f7ae0e9,02c04d3a5274952acdbc76987f3184b346a483d43be40874624b29e3692c1df5af,02ed06e0f418b5b43a7ec01d1d7d27290fa15f75771cb69b642a51471c29c84acd,036d46073cbb9ffee90473f3da429abc8de7f8751199da44485682a989a4bebb24,02f5d1ff7c9029a80a4e36b9a5497027ef7f3e73384a4a94fbfe7c4e9164eec8bc,02e41deffd1b7cce11cde209a781adcffdabd1b91c0ba0375857a2bfd9302419f3,02d76625f7956a7fc505ab02556c23ee72d832f1bac391bcd2d3abce5710a13d06,0399eb0a5487515802dc14544cf10b3666623762fbed2ec38a3975716e2c29c232,02bc2feaa536991d269aae46abb8f3772a5b3ad592314945e51543e7da84c4af6e,0318bf32e5217c1eb771a6d5ce1cd39395dff7ff665704f175c9a5451d95a2f2ca,02c681a6243f16208c2004bb81f5a8a67edfdd3e3711534eadeec3dcf0b010c759,0249fdd6b69768b8d84b4893f8ff84b36835c50183de20fcae8f366a45290d01fd)))", SIGNABLE, {{"a914c2c9c510e9d7f92fd6131e94803a8d34a8ef675e87"}}, OutputType::P2SH_SEGWIT, /*op_desc_id=*/uint256S("69c3f3153ed2527d12cf78e53e719233fdb7fa6ca9f8a10059ce47d34b49c4cb")); // Even if it's wrapped into P2SH
+ Check("wsh(multi(20,KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy,KwGNz6YCCQtYvFzMtrC6D3tKTKdBBboMrLTsjr2NYVBwapCkn7Mr,KxogYhiNfwxuswvXV66eFyKcCpm7dZ7TqHVqujHAVUjJxyivxQ9X,L2BUNduTSyZwZjwNHynQTF14mv2uz2NRq5n5sYWTb4FkkmqgEE9f,L1okJGHGn1kFjdXHKxXjwVVtmCMR2JA5QsbKCSpSb7ReQjezKeoD,KxDCNSST75HFPaW5QKpzHtAyaCQC7p9Vo3FYfi2u4dXD1vgMiboK,L5edQjFtnkcf5UWURn6UuuoFrabgDQUHdheKCziwN42aLwS3KizU,KzF8UWFcEC7BYTq8Go1xVimMkDmyNYVmXV5PV7RuDicvAocoPB8i,L3nHUboKG2w4VSJ5jYZ5CBM97oeK6YuKvfZxrefdShECcjEYKMWZ,KyjHo36dWkYhimKmVVmQTq3gERv3pnqA4xFCpvUgbGDJad7eS8WE,KwsfyHKRUTZPQtysN7M3tZ4GXTnuov5XRgjdF2XCG8faAPmFruRF,KzCUbGhN9LJhdeFfL9zQgTJMjqxdBKEekRGZX24hXdgCNCijkkap,KzgpMBwwsDLwkaC5UrmBgCYaBD2WgZ7PBoGYXR8KT7gCA9UTN5a3,KyBXTPy4T7YG4q9tcAM3LkvfRpD1ybHMvcJ2ehaWXaSqeGUxEdkP,KzJDe9iwJRPtKP2F2AoN6zBgzS7uiuAwhWCfGdNeYJ3PC1HNJ8M8,L1xbHrxynrqLKkoYc4qtoQPx6uy5qYXR5ZDYVYBSRmCV5piU3JG9,KzRedjSwMggebB3VufhbzpYJnvHfHe9kPJSjCU5QpJdAW3NSZxYS,Kyjtp5858xL7JfeV4PNRCKy2t6XvgqNNepArGY9F9F1SSPqNEMs3,L2D4RLHPiHBidkHS8ftx11jJk1hGFELvxh8LoxNQheaGT58dKenW,KyLPZdwY4td98bKkXqEXTEBX3vwEYTQo1yyLjX2jKXA63GBpmSjv))","wsh(multi(20,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8,02da72e8b46901a65d4374fe6315538d8f368557dda3a1dcf9ea903f3afe7314c8,0318c82dd0b53fd3a932d16e0ba9e278fcc937c582d5781be626ff16e201f72286,0297ccef1ef99f9d73dec9ad37476ddb232f1238aff877af19e72ba04493361009,02e502cfd5c3f972fe9a3e2a18827820638f96b6f347e54d63deb839011fd5765d,03e687710f0e3ebe81c1037074da939d409c0025f17eb86adb9427d28f0f7ae0e9,02c04d3a5274952acdbc76987f3184b346a483d43be40874624b29e3692c1df5af,02ed06e0f418b5b43a7ec01d1d7d27290fa15f75771cb69b642a51471c29c84acd,036d46073cbb9ffee90473f3da429abc8de7f8751199da44485682a989a4bebb24,02f5d1ff7c9029a80a4e36b9a5497027ef7f3e73384a4a94fbfe7c4e9164eec8bc,02e41deffd1b7cce11cde209a781adcffdabd1b91c0ba0375857a2bfd9302419f3,02d76625f7956a7fc505ab02556c23ee72d832f1bac391bcd2d3abce5710a13d06,0399eb0a5487515802dc14544cf10b3666623762fbed2ec38a3975716e2c29c232,02bc2feaa536991d269aae46abb8f3772a5b3ad592314945e51543e7da84c4af6e,0318bf32e5217c1eb771a6d5ce1cd39395dff7ff665704f175c9a5451d95a2f2ca,02c681a6243f16208c2004bb81f5a8a67edfdd3e3711534eadeec3dcf0b010c759,0249fdd6b69768b8d84b4893f8ff84b36835c50183de20fcae8f366a45290d01fd))", "wsh(multi(20,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8,02da72e8b46901a65d4374fe6315538d8f368557dda3a1dcf9ea903f3afe7314c8,0318c82dd0b53fd3a932d16e0ba9e278fcc937c582d5781be626ff16e201f72286,0297ccef1ef99f9d73dec9ad37476ddb232f1238aff877af19e72ba04493361009,02e502cfd5c3f972fe9a3e2a18827820638f96b6f347e54d63deb839011fd5765d,03e687710f0e3ebe81c1037074da939d409c0025f17eb86adb9427d28f0f7ae0e9,02c04d3a5274952acdbc76987f3184b346a483d43be40874624b29e3692c1df5af,02ed06e0f418b5b43a7ec01d1d7d27290fa15f75771cb69b642a51471c29c84acd,036d46073cbb9ffee90473f3da429abc8de7f8751199da44485682a989a4bebb24,02f5d1ff7c9029a80a4e36b9a5497027ef7f3e73384a4a94fbfe7c4e9164eec8bc,02e41deffd1b7cce11cde209a781adcffdabd1b91c0ba0375857a2bfd9302419f3,02d76625f7956a7fc505ab02556c23ee72d832f1bac391bcd2d3abce5710a13d06,0399eb0a5487515802dc14544cf10b3666623762fbed2ec38a3975716e2c29c232,02bc2feaa536991d269aae46abb8f3772a5b3ad592314945e51543e7da84c4af6e,0318bf32e5217c1eb771a6d5ce1cd39395dff7ff665704f175c9a5451d95a2f2ca,02c681a6243f16208c2004bb81f5a8a67edfdd3e3711534eadeec3dcf0b010c759,0249fdd6b69768b8d84b4893f8ff84b36835c50183de20fcae8f366a45290d01fd))", SIGNABLE, {{"0020376bd8344b8b6ebe504ff85ef743eaa1aa9272178223bcb6887e9378efb341ac"}}, OutputType::BECH32, /*op_desc_id=*/uint256{"2bb9d418ebdc3a75c465383985881527f3e5d6e520fb3efb152d4191b80e8412"}); // In P2WSH we can have up to 20 keys
+ Check("sh(wsh(multi(20,KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy,KwGNz6YCCQtYvFzMtrC6D3tKTKdBBboMrLTsjr2NYVBwapCkn7Mr,KxogYhiNfwxuswvXV66eFyKcCpm7dZ7TqHVqujHAVUjJxyivxQ9X,L2BUNduTSyZwZjwNHynQTF14mv2uz2NRq5n5sYWTb4FkkmqgEE9f,L1okJGHGn1kFjdXHKxXjwVVtmCMR2JA5QsbKCSpSb7ReQjezKeoD,KxDCNSST75HFPaW5QKpzHtAyaCQC7p9Vo3FYfi2u4dXD1vgMiboK,L5edQjFtnkcf5UWURn6UuuoFrabgDQUHdheKCziwN42aLwS3KizU,KzF8UWFcEC7BYTq8Go1xVimMkDmyNYVmXV5PV7RuDicvAocoPB8i,L3nHUboKG2w4VSJ5jYZ5CBM97oeK6YuKvfZxrefdShECcjEYKMWZ,KyjHo36dWkYhimKmVVmQTq3gERv3pnqA4xFCpvUgbGDJad7eS8WE,KwsfyHKRUTZPQtysN7M3tZ4GXTnuov5XRgjdF2XCG8faAPmFruRF,KzCUbGhN9LJhdeFfL9zQgTJMjqxdBKEekRGZX24hXdgCNCijkkap,KzgpMBwwsDLwkaC5UrmBgCYaBD2WgZ7PBoGYXR8KT7gCA9UTN5a3,KyBXTPy4T7YG4q9tcAM3LkvfRpD1ybHMvcJ2ehaWXaSqeGUxEdkP,KzJDe9iwJRPtKP2F2AoN6zBgzS7uiuAwhWCfGdNeYJ3PC1HNJ8M8,L1xbHrxynrqLKkoYc4qtoQPx6uy5qYXR5ZDYVYBSRmCV5piU3JG9,KzRedjSwMggebB3VufhbzpYJnvHfHe9kPJSjCU5QpJdAW3NSZxYS,Kyjtp5858xL7JfeV4PNRCKy2t6XvgqNNepArGY9F9F1SSPqNEMs3,L2D4RLHPiHBidkHS8ftx11jJk1hGFELvxh8LoxNQheaGT58dKenW,KyLPZdwY4td98bKkXqEXTEBX3vwEYTQo1yyLjX2jKXA63GBpmSjv)))","sh(wsh(multi(20,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8,02da72e8b46901a65d4374fe6315538d8f368557dda3a1dcf9ea903f3afe7314c8,0318c82dd0b53fd3a932d16e0ba9e278fcc937c582d5781be626ff16e201f72286,0297ccef1ef99f9d73dec9ad37476ddb232f1238aff877af19e72ba04493361009,02e502cfd5c3f972fe9a3e2a18827820638f96b6f347e54d63deb839011fd5765d,03e687710f0e3ebe81c1037074da939d409c0025f17eb86adb9427d28f0f7ae0e9,02c04d3a5274952acdbc76987f3184b346a483d43be40874624b29e3692c1df5af,02ed06e0f418b5b43a7ec01d1d7d27290fa15f75771cb69b642a51471c29c84acd,036d46073cbb9ffee90473f3da429abc8de7f8751199da44485682a989a4bebb24,02f5d1ff7c9029a80a4e36b9a5497027ef7f3e73384a4a94fbfe7c4e9164eec8bc,02e41deffd1b7cce11cde209a781adcffdabd1b91c0ba0375857a2bfd9302419f3,02d76625f7956a7fc505ab02556c23ee72d832f1bac391bcd2d3abce5710a13d06,0399eb0a5487515802dc14544cf10b3666623762fbed2ec38a3975716e2c29c232,02bc2feaa536991d269aae46abb8f3772a5b3ad592314945e51543e7da84c4af6e,0318bf32e5217c1eb771a6d5ce1cd39395dff7ff665704f175c9a5451d95a2f2ca,02c681a6243f16208c2004bb81f5a8a67edfdd3e3711534eadeec3dcf0b010c759,0249fdd6b69768b8d84b4893f8ff84b36835c50183de20fcae8f366a45290d01fd)))", "sh(wsh(multi(20,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8,02da72e8b46901a65d4374fe6315538d8f368557dda3a1dcf9ea903f3afe7314c8,0318c82dd0b53fd3a932d16e0ba9e278fcc937c582d5781be626ff16e201f72286,0297ccef1ef99f9d73dec9ad37476ddb232f1238aff877af19e72ba04493361009,02e502cfd5c3f972fe9a3e2a18827820638f96b6f347e54d63deb839011fd5765d,03e687710f0e3ebe81c1037074da939d409c0025f17eb86adb9427d28f0f7ae0e9,02c04d3a5274952acdbc76987f3184b346a483d43be40874624b29e3692c1df5af,02ed06e0f418b5b43a7ec01d1d7d27290fa15f75771cb69b642a51471c29c84acd,036d46073cbb9ffee90473f3da429abc8de7f8751199da44485682a989a4bebb24,02f5d1ff7c9029a80a4e36b9a5497027ef7f3e73384a4a94fbfe7c4e9164eec8bc,02e41deffd1b7cce11cde209a781adcffdabd1b91c0ba0375857a2bfd9302419f3,02d76625f7956a7fc505ab02556c23ee72d832f1bac391bcd2d3abce5710a13d06,0399eb0a5487515802dc14544cf10b3666623762fbed2ec38a3975716e2c29c232,02bc2feaa536991d269aae46abb8f3772a5b3ad592314945e51543e7da84c4af6e,0318bf32e5217c1eb771a6d5ce1cd39395dff7ff665704f175c9a5451d95a2f2ca,02c681a6243f16208c2004bb81f5a8a67edfdd3e3711534eadeec3dcf0b010c759,0249fdd6b69768b8d84b4893f8ff84b36835c50183de20fcae8f366a45290d01fd)))", SIGNABLE, {{"a914c2c9c510e9d7f92fd6131e94803a8d34a8ef675e87"}}, OutputType::P2SH_SEGWIT, /*op_desc_id=*/uint256{"69c3f3153ed2527d12cf78e53e719233fdb7fa6ca9f8a10059ce47d34b49c4cb"}); // Even if it's wrapped into P2SH
// Check for invalid nesting of structures
CheckUnparsable("sh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "sh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "A function is needed within P2SH"); // P2SH needs a script, not a key
CheckUnparsable("sh(combo(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "sh(combo(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", "Can only have combo() at top level"); // Old must be top level
@@ -525,8 +525,8 @@ BOOST_AUTO_TEST_CASE(descriptor_test)
CheckUnparsable("wsh(wsh(pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)))", "wsh(wsh(pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)))", "Can only have wsh() at top level or inside sh()"); // Cannot embed P2WSH inside P2WSH
// Checksums
- Check("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))#ggrsrxfy", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))#tjg09x5t", "sh(multi(2,[00000000/111h/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))#hgmsckna", DEFAULT, {{"a91445a9a622a8b0a1269944be477640eedc447bbd8487"}}, OutputType::LEGACY, /*op_desc_id=*/uint256S("9339b7bfbe8cfd9d0d55819778ef77f52e5786e85b4c83be8a0d5b976e033f4c"), {{0x8000006FUL,222},{0}});
- Check("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))", "sh(multi(2,[00000000/111h/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))", DEFAULT, {{"a91445a9a622a8b0a1269944be477640eedc447bbd8487"}}, OutputType::LEGACY, /*op_desc_id=*/uint256S("9339b7bfbe8cfd9d0d55819778ef77f52e5786e85b4c83be8a0d5b976e033f4c"), {{0x8000006FUL,222},{0}});
+ Check("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))#ggrsrxfy", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))#tjg09x5t", "sh(multi(2,[00000000/111h/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))#hgmsckna", DEFAULT, {{"a91445a9a622a8b0a1269944be477640eedc447bbd8487"}}, OutputType::LEGACY, /*op_desc_id=*/uint256{"9339b7bfbe8cfd9d0d55819778ef77f52e5786e85b4c83be8a0d5b976e033f4c"}, {{0x8000006FUL,222},{0}});
+ Check("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))", "sh(multi(2,[00000000/111h/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))", DEFAULT, {{"a91445a9a622a8b0a1269944be477640eedc447bbd8487"}}, OutputType::LEGACY, /*op_desc_id=*/uint256{"9339b7bfbe8cfd9d0d55819778ef77f52e5786e85b4c83be8a0d5b976e033f4c"}, {{0x8000006FUL,222},{0}});
CheckUnparsable("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))#", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))#", "Expected 8 character checksum, not 0 characters"); // Empty checksum
CheckUnparsable("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))#ggrsrxfyq", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))#tjg09x5tq", "Expected 8 character checksum, not 9 characters"); // Too long checksum
CheckUnparsable("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))#ggrsrxf", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))#tjg09x5", "Expected 8 character checksum, not 7 characters"); // Too short checksum
@@ -546,7 +546,7 @@ BOOST_AUTO_TEST_CASE(descriptor_test)
RANGE | HARDENED | XONLY_KEYS,
{{"51205172af752f057d543ce8e4a6f8dcf15548ec6be44041bfa93b72e191cfc8c1ee"}, {"51201b66f20b86f700c945ecb9ad9b0ad1662b73084e2bfea48bee02126350b8a5b1"}, {"512063e70f66d815218abcc2306aa930aaca07c5cde73b75127eb27b5e8c16b58a25"}},
OutputType::BECH32M,
- /*op_desc_id=*/uint256S("458f0e7f4075a81c990c6be6d5b985027ac8b7f7cef8311696d95b7b49658c7d"),
+ /*op_desc_id=*/uint256{"458f0e7f4075a81c990c6be6d5b985027ac8b7f7cef8311696d95b7b49658c7d"},
{{0x80000056, 0x80000001, 0x80000000, 1, 0}, {0x80000056, 0x80000001, 0x80000000, 1, 1}, {0x80000056, 0x80000001, 0x80000000, 1, 2}});
Check(
@@ -556,7 +556,7 @@ BOOST_AUTO_TEST_CASE(descriptor_test)
SIGNABLE | XONLY_KEYS,
{{"5120a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd"}},
OutputType::BECH32M,
- /*op_desc_id=*/uint256S("5ba3f7d83cee4795df00e0eaa5070a3e164283c5fc6e8586fd710eaa7a4168ec"));
+ /*op_desc_id=*/uint256{"5ba3f7d83cee4795df00e0eaa5070a3e164283c5fc6e8586fd710eaa7a4168ec"});
CheckUnparsable(
"",
@@ -607,31 +607,31 @@ BOOST_AUTO_TEST_CASE(descriptor_test)
CheckUnparsable("wsh(and_b(and_b(older(1),a:older(100000000)),s:pk(L4gM1FBdyHNpkzsFh9ipnofLhpZRp2mwobpeULy1a6dBTvw8Ywtd)))", "wsh(and_b(and_b(older(1),a:older(100000000)),s:pk(03cdabb7f2dce7bfbd8a0b9570c6fd1e712e5d64045e9d6b517b3d5072251dc204)))", "and_b(older(1),a:older(100000000)) is not sane: contains mixes of timelocks expressed in blocks and seconds");
CheckUnparsable("wsh(and_b(or_b(pkh(L4gM1FBdyHNpkzsFh9ipnofLhpZRp2mwobpeULy1a6dBTvw8Ywtd),s:pk(Kx9HCDjGiwFcgVNhTrS5z5NeZdD6veeam61eDxLDCkGWujvL4Gnn)),s:pk(L4gM1FBdyHNpkzsFh9ipnofLhpZRp2mwobpeULy1a6dBTvw8Ywtd)))", "wsh(and_b(or_b(pkh(03cdabb7f2dce7bfbd8a0b9570c6fd1e712e5d64045e9d6b517b3d5072251dc204),s:pk(032707170c71d8f75e4ca4e3fce870b9409dcaf12b051d3bcadff74747fa7619c0)),s:pk(03cdabb7f2dce7bfbd8a0b9570c6fd1e712e5d64045e9d6b517b3d5072251dc204)))", "and_b(or_b(pkh(03cdabb7f2dce7bfbd8a0b9570c6fd1e712e5d64045e9d6b517b3d5072251dc204),s:pk(032707170c71d8f75e4ca4e3fce870b9409dcaf12b051d3bcadff74747fa7619c0)),s:pk(03cdabb7f2dce7bfbd8a0b9570c6fd1e712e5d64045e9d6b517b3d5072251dc204)) is not sane: contains duplicate public keys");
// Valid with extended keys.
- Check("wsh(and_v(v:ripemd160(095ff41131e5946f3c85f79e44adbcf8e27e080e),multi(1,xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0)))", "wsh(and_v(v:ripemd160(095ff41131e5946f3c85f79e44adbcf8e27e080e),multi(1,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0)))", "wsh(and_v(v:ripemd160(095ff41131e5946f3c85f79e44adbcf8e27e080e),multi(1,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0)))", DEFAULT, {{"0020acf425291b98a1d7e0d4690139442abc289175be32ef1f75945e339924246d73"}}, OutputType::BECH32, /*op_desc_id=*/uint256S("0634b326edc66f9e2660562564d7a8fcca55f91dc4555ce0a51883cc72e0fa41"), {{},{0}});
+ Check("wsh(and_v(v:ripemd160(095ff41131e5946f3c85f79e44adbcf8e27e080e),multi(1,xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0)))", "wsh(and_v(v:ripemd160(095ff41131e5946f3c85f79e44adbcf8e27e080e),multi(1,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0)))", "wsh(and_v(v:ripemd160(095ff41131e5946f3c85f79e44adbcf8e27e080e),multi(1,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0)))", DEFAULT, {{"0020acf425291b98a1d7e0d4690139442abc289175be32ef1f75945e339924246d73"}}, OutputType::BECH32, /*op_desc_id=*/uint256{"0634b326edc66f9e2660562564d7a8fcca55f91dc4555ce0a51883cc72e0fa41"}, {{},{0}});
// Valid under sh(wsh()) and with a mix of xpubs and raw keys.
- Check("sh(wsh(thresh(1,pkh(L4gM1FBdyHNpkzsFh9ipnofLhpZRp2mwobpeULy1a6dBTvw8Ywtd),a:and_n(multi(1,xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0),n:older(2)))))", "sh(wsh(thresh(1,pkh(03cdabb7f2dce7bfbd8a0b9570c6fd1e712e5d64045e9d6b517b3d5072251dc204),a:and_n(multi(1,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0),n:older(2)))))", "sh(wsh(thresh(1,pkh(03cdabb7f2dce7bfbd8a0b9570c6fd1e712e5d64045e9d6b517b3d5072251dc204),a:and_n(multi(1,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0),n:older(2)))))", SIGNABLE | MIXED_PUBKEYS, {{"a914767e9119ff3b3ac0cb6dcfe21de1842ccf85f1c487"}}, OutputType::P2SH_SEGWIT, /*op_desc_id=*/uint256S("3cfcad33bc25579d70b23ce634d317be00a4e5400e758e37c215bdc17b31bfb8"), {{},{0}});
+ Check("sh(wsh(thresh(1,pkh(L4gM1FBdyHNpkzsFh9ipnofLhpZRp2mwobpeULy1a6dBTvw8Ywtd),a:and_n(multi(1,xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0),n:older(2)))))", "sh(wsh(thresh(1,pkh(03cdabb7f2dce7bfbd8a0b9570c6fd1e712e5d64045e9d6b517b3d5072251dc204),a:and_n(multi(1,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0),n:older(2)))))", "sh(wsh(thresh(1,pkh(03cdabb7f2dce7bfbd8a0b9570c6fd1e712e5d64045e9d6b517b3d5072251dc204),a:and_n(multi(1,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0),n:older(2)))))", SIGNABLE | MIXED_PUBKEYS, {{"a914767e9119ff3b3ac0cb6dcfe21de1842ccf85f1c487"}}, OutputType::P2SH_SEGWIT, /*op_desc_id=*/uint256{"3cfcad33bc25579d70b23ce634d317be00a4e5400e758e37c215bdc17b31bfb8"}, {{},{0}});
// An exotic multisig, we can sign for both branches
Check("wsh(thresh(1,pk(xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc),a:pkh(xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0)))", "wsh(thresh(1,pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL),a:pkh(xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0)))", "wsh(thresh(1,pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL),a:pkh(xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0)))", SIGNABLE, {{"00204a4528fbc0947e02e921b54bd476fc8cc2ebb5c6ae2ccf10ed29fe2937fb6892"}}, OutputType::BECH32, /*op_desc_id=*/std::nullopt, {{},{0}});
// We can sign for a script requiring the two kinds of timelock.
// But if we don't set a sequence high enough, we'll fail.
- Check("sh(wsh(thresh(2,ndv:after(1000),a:and_n(multi(1,xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0),n:older(2)))))", "sh(wsh(thresh(2,ndv:after(1000),a:and_n(multi(1,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0),n:older(2)))))", "sh(wsh(thresh(2,ndv:after(1000),a:and_n(multi(1,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0),n:older(2)))))", SIGNABLE_FAILS, {{"a914099f400961f930d4c16c3b33c0e2a58ef53ac38f87"}}, OutputType::P2SH_SEGWIT, /*op_desc_id=*/uint256S("f5c14a15b45d2af1b8ec69acfd3cf4790f069705d1b079efb0b8193fed181f64"), {{},{0}}, /*spender_nlocktime=*/1000, /*spender_nsequence=*/1);
+ Check("sh(wsh(thresh(2,ndv:after(1000),a:and_n(multi(1,xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0),n:older(2)))))", "sh(wsh(thresh(2,ndv:after(1000),a:and_n(multi(1,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0),n:older(2)))))", "sh(wsh(thresh(2,ndv:after(1000),a:and_n(multi(1,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0),n:older(2)))))", SIGNABLE_FAILS, {{"a914099f400961f930d4c16c3b33c0e2a58ef53ac38f87"}}, OutputType::P2SH_SEGWIT, /*op_desc_id=*/uint256{"f5c14a15b45d2af1b8ec69acfd3cf4790f069705d1b079efb0b8193fed181f64"}, {{},{0}}, /*spender_nlocktime=*/1000, /*spender_nsequence=*/1);
// And same for the nLockTime.
- Check("sh(wsh(thresh(2,ndv:after(1000),a:and_n(multi(1,xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0),n:older(2)))))", "sh(wsh(thresh(2,ndv:after(1000),a:and_n(multi(1,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0),n:older(2)))))", "sh(wsh(thresh(2,ndv:after(1000),a:and_n(multi(1,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0),n:older(2)))))", SIGNABLE_FAILS, {{"a914099f400961f930d4c16c3b33c0e2a58ef53ac38f87"}}, OutputType::P2SH_SEGWIT, /*op_desc_id=*/uint256S("f5c14a15b45d2af1b8ec69acfd3cf4790f069705d1b079efb0b8193fed181f64"), {{},{0}}, /*spender_nlocktime=*/999, /*spender_nsequence=*/2);
+ Check("sh(wsh(thresh(2,ndv:after(1000),a:and_n(multi(1,xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0),n:older(2)))))", "sh(wsh(thresh(2,ndv:after(1000),a:and_n(multi(1,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0),n:older(2)))))", "sh(wsh(thresh(2,ndv:after(1000),a:and_n(multi(1,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0),n:older(2)))))", SIGNABLE_FAILS, {{"a914099f400961f930d4c16c3b33c0e2a58ef53ac38f87"}}, OutputType::P2SH_SEGWIT, /*op_desc_id=*/uint256{"f5c14a15b45d2af1b8ec69acfd3cf4790f069705d1b079efb0b8193fed181f64"}, {{},{0}}, /*spender_nlocktime=*/999, /*spender_nsequence=*/2);
// But if both are set to (at least) the required value, we'll succeed.
- Check("sh(wsh(thresh(2,ndv:after(1000),a:and_n(multi(1,xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0),n:older(2)))))", "sh(wsh(thresh(2,ndv:after(1000),a:and_n(multi(1,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0),n:older(2)))))", "sh(wsh(thresh(2,ndv:after(1000),a:and_n(multi(1,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0),n:older(2)))))", SIGNABLE, {{"a914099f400961f930d4c16c3b33c0e2a58ef53ac38f87"}}, OutputType::P2SH_SEGWIT, /*op_desc_id=*/uint256S("f5c14a15b45d2af1b8ec69acfd3cf4790f069705d1b079efb0b8193fed181f64"), {{},{0}}, /*spender_nlocktime=*/1000, /*spender_nsequence=*/2);
+ Check("sh(wsh(thresh(2,ndv:after(1000),a:and_n(multi(1,xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0),n:older(2)))))", "sh(wsh(thresh(2,ndv:after(1000),a:and_n(multi(1,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0),n:older(2)))))", "sh(wsh(thresh(2,ndv:after(1000),a:and_n(multi(1,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0),n:older(2)))))", SIGNABLE, {{"a914099f400961f930d4c16c3b33c0e2a58ef53ac38f87"}}, OutputType::P2SH_SEGWIT, /*op_desc_id=*/uint256{"f5c14a15b45d2af1b8ec69acfd3cf4790f069705d1b079efb0b8193fed181f64"}, {{},{0}}, /*spender_nlocktime=*/1000, /*spender_nsequence=*/2);
// We can't sign for a script requiring a ripemd160 preimage without providing it.
- Check("wsh(and_v(v:ripemd160(ff9aa1829c90d26e73301383f549e1497b7d6325),pk(xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc)))", "wsh(and_v(v:ripemd160(ff9aa1829c90d26e73301383f549e1497b7d6325),pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)))", "wsh(and_v(v:ripemd160(ff9aa1829c90d26e73301383f549e1497b7d6325),pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)))", SIGNABLE_FAILS, {{"002001549deda34cbc4a5982263191380f522695a2ddc2f99fc3a65c736264bd6cab"}}, OutputType::BECH32, /*op_desc_id=*/uint256S("1fed6fbd0e408eb4bddfefa075289dc7061e7a3240c84f6ba5b9f294d96a21f4"), {{}}, /*spender_nlocktime=*/0, /*spender_nsequence=*/CTxIn::SEQUENCE_FINAL, {});
+ Check("wsh(and_v(v:ripemd160(ff9aa1829c90d26e73301383f549e1497b7d6325),pk(xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc)))", "wsh(and_v(v:ripemd160(ff9aa1829c90d26e73301383f549e1497b7d6325),pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)))", "wsh(and_v(v:ripemd160(ff9aa1829c90d26e73301383f549e1497b7d6325),pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)))", SIGNABLE_FAILS, {{"002001549deda34cbc4a5982263191380f522695a2ddc2f99fc3a65c736264bd6cab"}}, OutputType::BECH32, /*op_desc_id=*/uint256{"1fed6fbd0e408eb4bddfefa075289dc7061e7a3240c84f6ba5b9f294d96a21f4"}, {{}}, /*spender_nlocktime=*/0, /*spender_nsequence=*/CTxIn::SEQUENCE_FINAL, {});
// But if we provide it, we can.
- Check("wsh(and_v(v:ripemd160(ff9aa1829c90d26e73301383f549e1497b7d6325),pk(xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc)))", "wsh(and_v(v:ripemd160(ff9aa1829c90d26e73301383f549e1497b7d6325),pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)))", "wsh(and_v(v:ripemd160(ff9aa1829c90d26e73301383f549e1497b7d6325),pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)))", SIGNABLE, {{"002001549deda34cbc4a5982263191380f522695a2ddc2f99fc3a65c736264bd6cab"}}, OutputType::BECH32, /*op_desc_id=*/uint256S("1fed6fbd0e408eb4bddfefa075289dc7061e7a3240c84f6ba5b9f294d96a21f4"), {{}}, /*spender_nlocktime=*/0, /*spender_nsequence=*/CTxIn::SEQUENCE_FINAL, {{ParseHex("ff9aa1829c90d26e73301383f549e1497b7d6325"), ParseHex("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f")}});
+ Check("wsh(and_v(v:ripemd160(ff9aa1829c90d26e73301383f549e1497b7d6325),pk(xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc)))", "wsh(and_v(v:ripemd160(ff9aa1829c90d26e73301383f549e1497b7d6325),pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)))", "wsh(and_v(v:ripemd160(ff9aa1829c90d26e73301383f549e1497b7d6325),pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)))", SIGNABLE, {{"002001549deda34cbc4a5982263191380f522695a2ddc2f99fc3a65c736264bd6cab"}}, OutputType::BECH32, /*op_desc_id=*/uint256{"1fed6fbd0e408eb4bddfefa075289dc7061e7a3240c84f6ba5b9f294d96a21f4"}, {{}}, /*spender_nlocktime=*/0, /*spender_nsequence=*/CTxIn::SEQUENCE_FINAL, {{ParseHex("ff9aa1829c90d26e73301383f549e1497b7d6325"), ParseHex("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f")}});
// Same for sha256
- Check("wsh(and_v(v:sha256(7426ba0604c3f8682c7016b44673f85c5bd9da2fa6c1080810cf53ae320c9863),pk(xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc)))", "wsh(and_v(v:sha256(7426ba0604c3f8682c7016b44673f85c5bd9da2fa6c1080810cf53ae320c9863),pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)))", "wsh(and_v(v:sha256(7426ba0604c3f8682c7016b44673f85c5bd9da2fa6c1080810cf53ae320c9863),pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)))", SIGNABLE_FAILS, {{"002071f7283dbbb9a55ed43a54cda16ba0efd0f16dc48fe200f299e57bb5d7be8dd4"}}, OutputType::BECH32, /*op_desc_id=*/uint256S("a1809a65ba5ca2f09a06c114d4881eed95d1b62f38743cf126cf71b2dd411374"), {{}}, /*spender_nlocktime=*/0, /*spender_nsequence=*/CTxIn::SEQUENCE_FINAL, {});
- Check("wsh(and_v(v:sha256(7426ba0604c3f8682c7016b44673f85c5bd9da2fa6c1080810cf53ae320c9863),pk(xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc)))", "wsh(and_v(v:sha256(7426ba0604c3f8682c7016b44673f85c5bd9da2fa6c1080810cf53ae320c9863),pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)))", "wsh(and_v(v:sha256(7426ba0604c3f8682c7016b44673f85c5bd9da2fa6c1080810cf53ae320c9863),pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)))", SIGNABLE, {{"002071f7283dbbb9a55ed43a54cda16ba0efd0f16dc48fe200f299e57bb5d7be8dd4"}}, OutputType::BECH32, /*op_desc_id=*/uint256S("a1809a65ba5ca2f09a06c114d4881eed95d1b62f38743cf126cf71b2dd411374"), {{}}, /*spender_nlocktime=*/0, /*spender_nsequence=*/CTxIn::SEQUENCE_FINAL, {{ParseHex("7426ba0604c3f8682c7016b44673f85c5bd9da2fa6c1080810cf53ae320c9863"), ParseHex("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f")}});
+ Check("wsh(and_v(v:sha256(7426ba0604c3f8682c7016b44673f85c5bd9da2fa6c1080810cf53ae320c9863),pk(xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc)))", "wsh(and_v(v:sha256(7426ba0604c3f8682c7016b44673f85c5bd9da2fa6c1080810cf53ae320c9863),pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)))", "wsh(and_v(v:sha256(7426ba0604c3f8682c7016b44673f85c5bd9da2fa6c1080810cf53ae320c9863),pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)))", SIGNABLE_FAILS, {{"002071f7283dbbb9a55ed43a54cda16ba0efd0f16dc48fe200f299e57bb5d7be8dd4"}}, OutputType::BECH32, /*op_desc_id=*/uint256{"a1809a65ba5ca2f09a06c114d4881eed95d1b62f38743cf126cf71b2dd411374"}, {{}}, /*spender_nlocktime=*/0, /*spender_nsequence=*/CTxIn::SEQUENCE_FINAL, {});
+ Check("wsh(and_v(v:sha256(7426ba0604c3f8682c7016b44673f85c5bd9da2fa6c1080810cf53ae320c9863),pk(xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc)))", "wsh(and_v(v:sha256(7426ba0604c3f8682c7016b44673f85c5bd9da2fa6c1080810cf53ae320c9863),pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)))", "wsh(and_v(v:sha256(7426ba0604c3f8682c7016b44673f85c5bd9da2fa6c1080810cf53ae320c9863),pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)))", SIGNABLE, {{"002071f7283dbbb9a55ed43a54cda16ba0efd0f16dc48fe200f299e57bb5d7be8dd4"}}, OutputType::BECH32, /*op_desc_id=*/uint256{"a1809a65ba5ca2f09a06c114d4881eed95d1b62f38743cf126cf71b2dd411374"}, {{}}, /*spender_nlocktime=*/0, /*spender_nsequence=*/CTxIn::SEQUENCE_FINAL, {{ParseHex("7426ba0604c3f8682c7016b44673f85c5bd9da2fa6c1080810cf53ae320c9863"), ParseHex("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f")}});
// Same for hash160
- Check("wsh(and_v(v:hash160(292e2df59e3a22109200beed0cdc84b12e66793e),pk(xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc)))", "wsh(and_v(v:hash160(292e2df59e3a22109200beed0cdc84b12e66793e),pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)))", "wsh(and_v(v:hash160(292e2df59e3a22109200beed0cdc84b12e66793e),pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)))", SIGNABLE_FAILS, {{"00209b9d5b45735d0e15df5b41d6594602d3de472262f7b75edc6cf5f3e3fa4e3ae4"}}, OutputType::BECH32, /*op_desc_id=*/uint256S("d7bdbc680503a585925eec72d11fc99396f51855d0a03fce53c90bed4c2e319f"), {{}}, /*spender_nlocktime=*/0, /*spender_nsequence=*/CTxIn::SEQUENCE_FINAL, {});
- Check("wsh(and_v(v:hash160(292e2df59e3a22109200beed0cdc84b12e66793e),pk(xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc)))", "wsh(and_v(v:hash160(292e2df59e3a22109200beed0cdc84b12e66793e),pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)))", "wsh(and_v(v:hash160(292e2df59e3a22109200beed0cdc84b12e66793e),pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)))", SIGNABLE, {{"00209b9d5b45735d0e15df5b41d6594602d3de472262f7b75edc6cf5f3e3fa4e3ae4"}}, OutputType::BECH32, /*op_desc_id=*/uint256S("d7bdbc680503a585925eec72d11fc99396f51855d0a03fce53c90bed4c2e319f"), {{}}, /*spender_nlocktime=*/0, /*spender_nsequence=*/CTxIn::SEQUENCE_FINAL, {{ParseHex("292e2df59e3a22109200beed0cdc84b12e66793e"), ParseHex("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f")}});
+ Check("wsh(and_v(v:hash160(292e2df59e3a22109200beed0cdc84b12e66793e),pk(xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc)))", "wsh(and_v(v:hash160(292e2df59e3a22109200beed0cdc84b12e66793e),pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)))", "wsh(and_v(v:hash160(292e2df59e3a22109200beed0cdc84b12e66793e),pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)))", SIGNABLE_FAILS, {{"00209b9d5b45735d0e15df5b41d6594602d3de472262f7b75edc6cf5f3e3fa4e3ae4"}}, OutputType::BECH32, /*op_desc_id=*/uint256{"d7bdbc680503a585925eec72d11fc99396f51855d0a03fce53c90bed4c2e319f"}, {{}}, /*spender_nlocktime=*/0, /*spender_nsequence=*/CTxIn::SEQUENCE_FINAL, {});
+ Check("wsh(and_v(v:hash160(292e2df59e3a22109200beed0cdc84b12e66793e),pk(xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc)))", "wsh(and_v(v:hash160(292e2df59e3a22109200beed0cdc84b12e66793e),pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)))", "wsh(and_v(v:hash160(292e2df59e3a22109200beed0cdc84b12e66793e),pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)))", SIGNABLE, {{"00209b9d5b45735d0e15df5b41d6594602d3de472262f7b75edc6cf5f3e3fa4e3ae4"}}, OutputType::BECH32, /*op_desc_id=*/uint256{"d7bdbc680503a585925eec72d11fc99396f51855d0a03fce53c90bed4c2e319f"}, {{}}, /*spender_nlocktime=*/0, /*spender_nsequence=*/CTxIn::SEQUENCE_FINAL, {{ParseHex("292e2df59e3a22109200beed0cdc84b12e66793e"), ParseHex("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f")}});
// Same for hash256
- Check("wsh(and_v(v:hash256(ae253ca2a54debcac7ecf414f6734f48c56421a08bb59182ff9f39a6fffdb588),pk(xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc)))", "wsh(and_v(v:hash256(ae253ca2a54debcac7ecf414f6734f48c56421a08bb59182ff9f39a6fffdb588),pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)))", "wsh(and_v(v:hash256(ae253ca2a54debcac7ecf414f6734f48c56421a08bb59182ff9f39a6fffdb588),pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)))", SIGNABLE_FAILS, {{"0020cf62bf97baf977aec69cbc290c372899f913337a9093e8f066ab59b8657a365c"}}, OutputType::BECH32, /*op_desc_id=*/uint256S("8412ba3ac20ba3a30f81442d10d32e0468fa52814960d04e959bf84a9b813b88"), {{}}, /*spender_nlocktime=*/0, /*spender_nsequence=*/CTxIn::SEQUENCE_FINAL, {});
- Check("wsh(and_v(v:hash256(ae253ca2a54debcac7ecf414f6734f48c56421a08bb59182ff9f39a6fffdb588),pk(xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc)))", "wsh(and_v(v:hash256(ae253ca2a54debcac7ecf414f6734f48c56421a08bb59182ff9f39a6fffdb588),pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)))", "wsh(and_v(v:hash256(ae253ca2a54debcac7ecf414f6734f48c56421a08bb59182ff9f39a6fffdb588),pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)))", SIGNABLE, {{"0020cf62bf97baf977aec69cbc290c372899f913337a9093e8f066ab59b8657a365c"}}, OutputType::BECH32, /*op_desc_id=*/uint256S("8412ba3ac20ba3a30f81442d10d32e0468fa52814960d04e959bf84a9b813b88"), {{}}, /*spender_nlocktime=*/0, /*spender_nsequence=*/CTxIn::SEQUENCE_FINAL, {{ParseHex("ae253ca2a54debcac7ecf414f6734f48c56421a08bb59182ff9f39a6fffdb588"), ParseHex("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f")}});
+ Check("wsh(and_v(v:hash256(ae253ca2a54debcac7ecf414f6734f48c56421a08bb59182ff9f39a6fffdb588),pk(xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc)))", "wsh(and_v(v:hash256(ae253ca2a54debcac7ecf414f6734f48c56421a08bb59182ff9f39a6fffdb588),pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)))", "wsh(and_v(v:hash256(ae253ca2a54debcac7ecf414f6734f48c56421a08bb59182ff9f39a6fffdb588),pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)))", SIGNABLE_FAILS, {{"0020cf62bf97baf977aec69cbc290c372899f913337a9093e8f066ab59b8657a365c"}}, OutputType::BECH32, /*op_desc_id=*/uint256{"8412ba3ac20ba3a30f81442d10d32e0468fa52814960d04e959bf84a9b813b88"}, {{}}, /*spender_nlocktime=*/0, /*spender_nsequence=*/CTxIn::SEQUENCE_FINAL, {});
+ Check("wsh(and_v(v:hash256(ae253ca2a54debcac7ecf414f6734f48c56421a08bb59182ff9f39a6fffdb588),pk(xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc)))", "wsh(and_v(v:hash256(ae253ca2a54debcac7ecf414f6734f48c56421a08bb59182ff9f39a6fffdb588),pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)))", "wsh(and_v(v:hash256(ae253ca2a54debcac7ecf414f6734f48c56421a08bb59182ff9f39a6fffdb588),pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)))", SIGNABLE, {{"0020cf62bf97baf977aec69cbc290c372899f913337a9093e8f066ab59b8657a365c"}}, OutputType::BECH32, /*op_desc_id=*/uint256{"8412ba3ac20ba3a30f81442d10d32e0468fa52814960d04e959bf84a9b813b88"}, {{}}, /*spender_nlocktime=*/0, /*spender_nsequence=*/CTxIn::SEQUENCE_FINAL, {{ParseHex("ae253ca2a54debcac7ecf414f6734f48c56421a08bb59182ff9f39a6fffdb588"), ParseHex("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f")}});
// Can have a Miniscript expression under tr() if it's alone.
Check("tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,thresh(2,pk(L1NKM8dVA1h52mwDrmk1YreTWkAZZTu2vmKLpmLEbFRqGQYjHeEV),s:pk(Kz3iCBy3HNGP5CZWDsAMmnCMFNwqdDohudVN9fvkrN7tAkzKNtM7),adv:older(42)))", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,thresh(2,pk(30a6069f344fb784a2b4c99540a91ee727c91e3a25ef6aae867d9c65b5f23529),s:pk(9918d400c1b8c3c478340a40117ced4054b6b58f48cdb3c89b836bdfee1f5766),adv:older(42)))", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,thresh(2,pk(30a6069f344fb784a2b4c99540a91ee727c91e3a25ef6aae867d9c65b5f23529),s:pk(9918d400c1b8c3c478340a40117ced4054b6b58f48cdb3c89b836bdfee1f5766),adv:older(42)))", MISSING_PRIVKEYS | XONLY_KEYS | SIGNABLE, {{"512033982eebe204dc66508e4b19cfc31b5ffc6e1bfcbf6e5597dfc2521a52270795"}}, OutputType::BECH32M);
// Can have a pkh() expression alone as tr() script path (because pkh() is valid Miniscript).
diff --git a/src/test/fuzz/addrman.cpp b/src/test/fuzz/addrman.cpp
index 8a54cc656d..dbec2bc858 100644
--- a/src/test/fuzz/addrman.cpp
+++ b/src/test/fuzz/addrman.cpp
@@ -124,7 +124,7 @@ public:
explicit AddrManDeterministic(const NetGroupManager& netgroupman, FuzzedDataProvider& fuzzed_data_provider)
: AddrMan(netgroupman, /*deterministic=*/true, GetCheckRatio())
{
- WITH_LOCK(m_impl->cs, m_impl->insecure_rand = FastRandomContext{ConsumeUInt256(fuzzed_data_provider)});
+ WITH_LOCK(m_impl->cs, m_impl->insecure_rand.Reseed(ConsumeUInt256(fuzzed_data_provider)));
}
/**
diff --git a/src/test/fuzz/bech32.cpp b/src/test/fuzz/bech32.cpp
index ffc5ba518f..daa6e24404 100644
--- a/src/test/fuzz/bech32.cpp
+++ b/src/test/fuzz/bech32.cpp
@@ -29,8 +29,9 @@ FUZZ_TARGET(bech32)
std::vector<unsigned char> input;
ConvertBits<8, 5, true>([&](unsigned char c) { input.push_back(c); }, buffer.begin(), buffer.end());
- if (input.size() + 3 + 6 <= 90) {
- // If it's possible to encode input in Bech32(m) without exceeding the 90-character limit:
+ // Input data part + 3 characters for the HRP and separator (bc1) + the checksum characters
+ if (input.size() + 3 + bech32::CHECKSUM_SIZE <= bech32::CharLimit::BECH32) {
+ // If it's possible to encode input in Bech32(m) without exceeding the bech32-character limit:
for (auto encoding : {bech32::Encoding::BECH32, bech32::Encoding::BECH32M}) {
const std::string encoded = bech32::Encode(encoding, "bc", input);
assert(!encoded.empty());
diff --git a/src/test/fuzz/bip324.cpp b/src/test/fuzz/bip324.cpp
index 8210e75cee..9892e7a81c 100644
--- a/src/test/fuzz/bip324.cpp
+++ b/src/test/fuzz/bip324.cpp
@@ -4,11 +4,11 @@
#include <bip324.h>
#include <chainparams.h>
+#include <random.h>
#include <span.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
-#include <test/util/xoroshiro128plusplus.h>
#include <cstdint>
#include <vector>
@@ -56,7 +56,7 @@ FUZZ_TARGET(bip324_cipher_roundtrip, .init=Initialize)
// (potentially buggy) edge cases triggered by specific values of contents/AAD, so we can avoid
// reading the actual data for those from the fuzzer input (which would need large amounts of
// data).
- XoRoShiRo128PlusPlus rng(provider.ConsumeIntegral<uint64_t>());
+ InsecureRandomContext rng(provider.ConsumeIntegral<uint64_t>());
// Compare session IDs and garbage terminators.
assert(initiator.GetSessionID() == responder.GetSessionID());
@@ -79,10 +79,8 @@ FUZZ_TARGET(bip324_cipher_roundtrip, .init=Initialize)
unsigned length_bits = 2 * ((mode >> 5) & 7);
unsigned length = provider.ConsumeIntegralInRange<unsigned>(0, (1 << length_bits) - 1);
// Generate aad and content.
- std::vector<std::byte> aad(aad_length);
- for (auto& val : aad) val = std::byte{(uint8_t)rng()};
- std::vector<std::byte> contents(length);
- for (auto& val : contents) val = std::byte{(uint8_t)rng()};
+ auto aad = rng.randbytes<std::byte>(aad_length);
+ auto contents = rng.randbytes<std::byte>(length);
// Pick sides.
auto& sender{from_init ? initiator : responder};
diff --git a/src/test/fuzz/bitset.cpp b/src/test/fuzz/bitset.cpp
index 7684337729..ce6be0499c 100644
--- a/src/test/fuzz/bitset.cpp
+++ b/src/test/fuzz/bitset.cpp
@@ -2,9 +2,9 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <random.h>
#include <span.h>
#include <test/fuzz/util.h>
-#include <test/util/xoroshiro128plusplus.h>
#include <util/bitset.h>
#include <bitset>
@@ -29,7 +29,7 @@ void TestType(FuzzBufferType buffer)
* bitsets and their simulations do not matter for the purpose of detecting edge cases, thus
* these are taken from a deterministically-seeded RNG instead. To provide some level of
* variation however, pick the seed based on the buffer size and size of the chosen bitset. */
- XoRoShiRo128PlusPlus rng(buffer.size() + 0x10000 * S::Size());
+ InsecureRandomContext rng(buffer.size() + 0x10000 * S::Size());
using Sim = std::bitset<S::Size()>;
// Up to 4 real BitSets (initially 2).
@@ -124,7 +124,7 @@ void TestType(FuzzBufferType buffer)
sim[dest].reset();
real[dest] = S{};
for (unsigned i = 0; i < S::Size(); ++i) {
- if (rng() & 1) {
+ if (rng.randbool()) {
sim[dest][i] = true;
real[dest].Set(i);
}
@@ -132,9 +132,9 @@ void TestType(FuzzBufferType buffer)
break;
} else if (dest < sim.size() && command-- == 0) {
/* Assign initializer list. */
- unsigned r1 = rng() % S::Size();
- unsigned r2 = rng() % S::Size();
- unsigned r3 = rng() % S::Size();
+ unsigned r1 = rng.randrange(S::Size());
+ unsigned r2 = rng.randrange(S::Size());
+ unsigned r3 = rng.randrange(S::Size());
compare_fn(dest);
sim[dest].reset();
real[dest] = {r1, r2, r3};
@@ -166,8 +166,8 @@ void TestType(FuzzBufferType buffer)
break;
} else if (sim.size() < 4 && command-- == 0) {
/* Construct with initializer list. */
- unsigned r1 = rng() % S::Size();
- unsigned r2 = rng() % S::Size();
+ unsigned r1 = rng.randrange(S::Size());
+ unsigned r2 = rng.randrange(S::Size());
sim.emplace_back();
sim.back().set(r1);
sim.back().set(r2);
diff --git a/src/test/fuzz/block_header.cpp b/src/test/fuzz/block_header.cpp
index c73270dcb3..2e446b16eb 100644
--- a/src/test/fuzz/block_header.cpp
+++ b/src/test/fuzz/block_header.cpp
@@ -23,7 +23,7 @@ FUZZ_TARGET(block_header)
}
{
const uint256 hash = block_header->GetHash();
- static const uint256 u256_max(uint256S("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"));
+ constexpr uint256 u256_max{"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"};
assert(hash != u256_max);
assert(block_header->GetBlockTime() == block_header->nTime);
assert(block_header->IsNull() == (block_header->nBits == 0));
diff --git a/src/test/fuzz/block_index.cpp b/src/test/fuzz/block_index.cpp
new file mode 100644
index 0000000000..eef8c2efc8
--- /dev/null
+++ b/src/test/fuzz/block_index.cpp
@@ -0,0 +1,133 @@
+// Copyright (c) 2023 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 <chain.h>
+#include <chainparams.h>
+#include <node/blockstorage.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+#include <test/util/setup_common.h>
+#include <txdb.h>
+#include <validation.h>
+
+namespace {
+
+const BasicTestingSetup* g_setup;
+
+// Hardcoded block hash and nBits to make sure the blocks we store pass the pow check.
+uint256 g_block_hash;
+
+bool operator==(const CBlockFileInfo& a, const CBlockFileInfo& b)
+{
+ return a.nBlocks == b.nBlocks &&
+ a.nSize == b.nSize &&
+ a.nUndoSize == b.nUndoSize &&
+ a.nHeightFirst == b.nHeightFirst &&
+ a.nHeightLast == b.nHeightLast &&
+ a.nTimeFirst == b.nTimeFirst &&
+ a.nTimeLast == b.nTimeLast;
+}
+
+CBlockHeader ConsumeBlockHeader(FuzzedDataProvider& provider)
+{
+ CBlockHeader header;
+ header.nVersion = provider.ConsumeIntegral<decltype(header.nVersion)>();
+ header.hashPrevBlock = g_block_hash;
+ header.hashMerkleRoot = g_block_hash;
+ header.nTime = provider.ConsumeIntegral<decltype(header.nTime)>();
+ header.nBits = Params().GenesisBlock().nBits;
+ header.nNonce = provider.ConsumeIntegral<decltype(header.nNonce)>();
+ return header;
+}
+
+} // namespace
+
+void init_block_index()
+{
+ static const auto testing_setup = MakeNoLogFileContext<>(ChainType::MAIN);
+ g_setup = testing_setup.get();
+ g_block_hash = Params().GenesisBlock().GetHash();
+}
+
+FUZZ_TARGET(block_index, .init = init_block_index)
+{
+ FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
+ auto block_index = kernel::BlockTreeDB(DBParams{
+ .path = "", // Memory only.
+ .cache_bytes = 1 << 20, // 1MB.
+ .memory_only = true,
+ });
+
+ // Generate a number of block files to be stored in the index.
+ int files_count = fuzzed_data_provider.ConsumeIntegralInRange(1, 100);
+ std::vector<std::unique_ptr<CBlockFileInfo>> files;
+ files.reserve(files_count);
+ std::vector<std::pair<int, const CBlockFileInfo*>> files_info;
+ files_info.reserve(files_count);
+ for (int i = 0; i < files_count; i++) {
+ if (auto file_info = ConsumeDeserializable<CBlockFileInfo>(fuzzed_data_provider)) {
+ files.push_back(std::make_unique<CBlockFileInfo>(std::move(*file_info)));
+ files_info.emplace_back(i, files.back().get());
+ } else {
+ return;
+ }
+ }
+
+ // Generate a number of block headers to be stored in the index.
+ int blocks_count = fuzzed_data_provider.ConsumeIntegralInRange(files_count * 10, files_count * 100);
+ std::vector<std::unique_ptr<CBlockIndex>> blocks;
+ blocks.reserve(blocks_count);
+ std::vector<const CBlockIndex*> blocks_info;
+ blocks_info.reserve(blocks_count);
+ for (int i = 0; i < blocks_count; i++) {
+ CBlockHeader header{ConsumeBlockHeader(fuzzed_data_provider)};
+ blocks.push_back(std::make_unique<CBlockIndex>(std::move(header)));
+ blocks.back()->phashBlock = &g_block_hash;
+ blocks_info.push_back(blocks.back().get());
+ }
+
+ // Store these files and blocks in the block index. It should not fail.
+ assert(block_index.WriteBatchSync(files_info, files_count - 1, blocks_info));
+
+ // We should be able to read every block file info we stored. Its value should correspond to
+ // what we stored above.
+ CBlockFileInfo info;
+ for (const auto& [n, file_info]: files_info) {
+ assert(block_index.ReadBlockFileInfo(n, info));
+ assert(info == *file_info);
+ }
+
+ // We should be able to read the last block file number. Its value should be consistent.
+ int last_block_file;
+ assert(block_index.ReadLastBlockFile(last_block_file));
+ assert(last_block_file == files_count - 1);
+
+ // We should be able to flip and read the reindexing flag.
+ bool reindexing;
+ block_index.WriteReindexing(true);
+ block_index.ReadReindexing(reindexing);
+ assert(reindexing);
+ block_index.WriteReindexing(false);
+ block_index.ReadReindexing(reindexing);
+ assert(!reindexing);
+
+ // We should be able to set and read the value of any random flag.
+ const std::string flag_name = fuzzed_data_provider.ConsumeRandomLengthString(100);
+ bool flag_value;
+ block_index.WriteFlag(flag_name, true);
+ block_index.ReadFlag(flag_name, flag_value);
+ assert(flag_value);
+ block_index.WriteFlag(flag_name, false);
+ block_index.ReadFlag(flag_name, flag_value);
+ assert(!flag_value);
+
+ // We should be able to load everything we've previously stored. Note to assert on the
+ // return value we need to make sure all blocks pass the pow check.
+ const auto params{Params().GetConsensus()};
+ const auto inserter = [&](const uint256&) {
+ return blocks.back().get();
+ };
+ WITH_LOCK(::cs_main, assert(block_index.LoadBlockIndexGuts(params, inserter, g_setup->m_interrupt)));
+}
diff --git a/src/test/fuzz/cluster_linearize.cpp b/src/test/fuzz/cluster_linearize.cpp
new file mode 100644
index 0000000000..2dfdfbb41d
--- /dev/null
+++ b/src/test/fuzz/cluster_linearize.cpp
@@ -0,0 +1,957 @@
+// Copyright (c) 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 <cluster_linearize.h>
+#include <serialize.h>
+#include <streams.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/util/cluster_linearize.h>
+#include <util/bitset.h>
+#include <util/feefrac.h>
+
+#include <algorithm>
+#include <stdint.h>
+#include <vector>
+#include <utility>
+
+using namespace cluster_linearize;
+
+namespace {
+
+/** A simple finder class for candidate sets.
+ *
+ * This class matches SearchCandidateFinder in interface and behavior, though with fewer
+ * optimizations.
+ */
+template<typename SetType>
+class SimpleCandidateFinder
+{
+ /** Internal dependency graph. */
+ const DepGraph<SetType>& m_depgraph;
+ /** Which transaction are left to include. */
+ SetType m_todo;
+
+public:
+ /** Construct an SimpleCandidateFinder for a given graph. */
+ SimpleCandidateFinder(const DepGraph<SetType>& depgraph LIFETIMEBOUND) noexcept :
+ m_depgraph(depgraph), m_todo{SetType::Fill(depgraph.TxCount())} {}
+
+ /** Remove a set of transactions from the set of to-be-linearized ones. */
+ void MarkDone(SetType select) noexcept { m_todo -= select; }
+
+ /** Determine whether unlinearized transactions remain. */
+ bool AllDone() const noexcept { return m_todo.None(); }
+
+ /** Find a candidate set using at most max_iterations iterations, and the number of iterations
+ * actually performed. If that number is less than max_iterations, then the result is optimal.
+ *
+ * Complexity: O(N * M), where M is the number of connected topological subsets of the cluster.
+ * That number is bounded by M <= 2^(N-1).
+ */
+ std::pair<SetInfo<SetType>, uint64_t> FindCandidateSet(uint64_t max_iterations) const noexcept
+ {
+ uint64_t iterations_left = max_iterations;
+ // Queue of work units. Each consists of:
+ // - inc: set of transactions definitely included
+ // - und: set of transactions that can be added to inc still
+ std::vector<std::pair<SetType, SetType>> queue;
+ // Initially we have just one queue element, with the entire graph in und.
+ queue.emplace_back(SetType{}, m_todo);
+ // Best solution so far.
+ SetInfo best(m_depgraph, m_todo);
+ // Process the queue.
+ while (!queue.empty() && iterations_left) {
+ --iterations_left;
+ // Pop top element of the queue.
+ auto [inc, und] = queue.back();
+ queue.pop_back();
+ // Look for a transaction to consider adding/removing.
+ bool inc_none = inc.None();
+ for (auto split : und) {
+ // If inc is empty, consider any split transaction. Otherwise only consider
+ // transactions that share ancestry with inc so far (which means only connected
+ // sets will be considered).
+ if (inc_none || inc.Overlaps(m_depgraph.Ancestors(split))) {
+ // Add a queue entry with split included.
+ SetInfo new_inc(m_depgraph, inc | (m_todo & m_depgraph.Ancestors(split)));
+ queue.emplace_back(new_inc.transactions, und - new_inc.transactions);
+ // Add a queue entry with split excluded.
+ queue.emplace_back(inc, und - m_depgraph.Descendants(split));
+ // Update statistics to account for the candidate new_inc.
+ if (new_inc.feerate > best.feerate) best = new_inc;
+ break;
+ }
+ }
+ }
+ return {std::move(best), max_iterations - iterations_left};
+ }
+};
+
+/** A very simple finder class for optimal candidate sets, which tries every subset.
+ *
+ * It is even simpler than SimpleCandidateFinder, and is primarily included here to test the
+ * correctness of SimpleCandidateFinder, which is then used to test the correctness of
+ * SearchCandidateFinder.
+ */
+template<typename SetType>
+class ExhaustiveCandidateFinder
+{
+ /** Internal dependency graph. */
+ const DepGraph<SetType>& m_depgraph;
+ /** Which transaction are left to include. */
+ SetType m_todo;
+
+public:
+ /** Construct an ExhaustiveCandidateFinder for a given graph. */
+ ExhaustiveCandidateFinder(const DepGraph<SetType>& depgraph LIFETIMEBOUND) noexcept :
+ m_depgraph(depgraph), m_todo{SetType::Fill(depgraph.TxCount())} {}
+
+ /** Remove a set of transactions from the set of to-be-linearized ones. */
+ void MarkDone(SetType select) noexcept { m_todo -= select; }
+
+ /** Determine whether unlinearized transactions remain. */
+ bool AllDone() const noexcept { return m_todo.None(); }
+
+ /** Find the optimal remaining candidate set.
+ *
+ * Complexity: O(N * 2^N).
+ */
+ SetInfo<SetType> FindCandidateSet() const noexcept
+ {
+ // Best solution so far.
+ SetInfo<SetType> best{m_todo, m_depgraph.FeeRate(m_todo)};
+ // The number of combinations to try.
+ uint64_t limit = (uint64_t{1} << m_todo.Count()) - 1;
+ // Try the transitive closure of every non-empty subset of m_todo.
+ for (uint64_t x = 1; x < limit; ++x) {
+ // If bit number b is set in x, then the remaining ancestors of the b'th remaining
+ // transaction in m_todo are included.
+ SetType txn;
+ auto x_shifted{x};
+ for (auto i : m_todo) {
+ if (x_shifted & 1) txn |= m_depgraph.Ancestors(i);
+ x_shifted >>= 1;
+ }
+ SetInfo cur(m_depgraph, txn & m_todo);
+ if (cur.feerate > best.feerate) best = cur;
+ }
+ return best;
+ }
+};
+
+/** A simple linearization algorithm.
+ *
+ * This matches Linearize() in interface and behavior, though with fewer optimizations, lacking
+ * the ability to pass in an existing linearization, and using just SimpleCandidateFinder rather
+ * than AncestorCandidateFinder and SearchCandidateFinder.
+ */
+template<typename SetType>
+std::pair<std::vector<ClusterIndex>, bool> SimpleLinearize(const DepGraph<SetType>& depgraph, uint64_t max_iterations)
+{
+ std::vector<ClusterIndex> linearization;
+ SimpleCandidateFinder finder(depgraph);
+ SetType todo = SetType::Fill(depgraph.TxCount());
+ bool optimal = true;
+ while (todo.Any()) {
+ auto [candidate, iterations_done] = finder.FindCandidateSet(max_iterations);
+ if (iterations_done == max_iterations) optimal = false;
+ depgraph.AppendTopo(linearization, candidate.transactions);
+ todo -= candidate.transactions;
+ finder.MarkDone(candidate.transactions);
+ max_iterations -= iterations_done;
+ }
+ return {std::move(linearization), optimal};
+}
+
+/** Given a dependency graph, and a todo set, read a topological subset of todo from reader. */
+template<typename SetType>
+SetType ReadTopologicalSet(const DepGraph<SetType>& depgraph, const SetType& todo, SpanReader& reader)
+{
+ uint64_t mask{0};
+ try {
+ reader >> VARINT(mask);
+ } catch(const std::ios_base::failure&) {}
+ SetType ret;
+ for (auto i : todo) {
+ if (!ret[i]) {
+ if (mask & 1) ret |= depgraph.Ancestors(i);
+ mask >>= 1;
+ }
+ }
+ return ret & todo;
+}
+
+/** Given a dependency graph, construct any valid linearization for it, reading from a SpanReader. */
+template<typename BS>
+std::vector<ClusterIndex> ReadLinearization(const DepGraph<BS>& depgraph, SpanReader& reader)
+{
+ std::vector<ClusterIndex> linearization;
+ TestBitSet todo = TestBitSet::Fill(depgraph.TxCount());
+ // In every iteration one topologically-valid transaction is appended to linearization.
+ while (todo.Any()) {
+ // Compute the set of transactions with no not-yet-included ancestors.
+ TestBitSet potential_next;
+ for (auto j : todo) {
+ if ((depgraph.Ancestors(j) & todo) == TestBitSet::Singleton(j)) {
+ potential_next.Set(j);
+ }
+ }
+ // There must always be one (otherwise there is a cycle in the graph).
+ assert(potential_next.Any());
+ // Read a number from reader, and interpret it as index into potential_next.
+ uint64_t idx{0};
+ try {
+ reader >> VARINT(idx);
+ } catch (const std::ios_base::failure&) {}
+ idx %= potential_next.Count();
+ // Find out which transaction that corresponds to.
+ for (auto j : potential_next) {
+ if (idx == 0) {
+ // When found, add it to linearization and remove it from todo.
+ linearization.push_back(j);
+ assert(todo[j]);
+ todo.Reset(j);
+ break;
+ }
+ --idx;
+ }
+ }
+ return linearization;
+}
+
+} // namespace
+
+FUZZ_TARGET(clusterlin_add_dependency)
+{
+ // Verify that computing a DepGraph from a cluster, or building it step by step using AddDependency
+ // have the same effect.
+
+ // Construct a cluster of a certain length, with no dependencies.
+ FuzzedDataProvider provider(buffer.data(), buffer.size());
+ auto num_tx = provider.ConsumeIntegralInRange<ClusterIndex>(2, 32);
+ Cluster<TestBitSet> cluster(num_tx, std::pair{FeeFrac{0, 1}, TestBitSet{}});
+ // Construct the corresponding DepGraph object (also no dependencies).
+ DepGraph depgraph(cluster);
+ SanityCheck(depgraph);
+ // Read (parent, child) pairs, and add them to the cluster and depgraph.
+ LIMITED_WHILE(provider.remaining_bytes() > 0, TestBitSet::Size() * TestBitSet::Size()) {
+ auto parent = provider.ConsumeIntegralInRange<ClusterIndex>(0, num_tx - 1);
+ auto child = provider.ConsumeIntegralInRange<ClusterIndex>(0, num_tx - 2);
+ child += (child >= parent);
+ cluster[child].second.Set(parent);
+ depgraph.AddDependency(parent, child);
+ assert(depgraph.Ancestors(child)[parent]);
+ assert(depgraph.Descendants(parent)[child]);
+ }
+ // Sanity check the result.
+ SanityCheck(depgraph);
+ // Verify that the resulting DepGraph matches one recomputed from the cluster.
+ assert(DepGraph(cluster) == depgraph);
+}
+
+FUZZ_TARGET(clusterlin_cluster_serialization)
+{
+ // Verify that any graph of transactions has its ancestry correctly computed by DepGraph, and
+ // if it is a DAG, that it can be serialized as a DepGraph in a way that roundtrips. This
+ // guarantees that any acyclic cluster has a corresponding DepGraph serialization.
+
+ FuzzedDataProvider provider(buffer.data(), buffer.size());
+
+ // Construct a cluster in a naive way (using a FuzzedDataProvider-based serialization).
+ Cluster<TestBitSet> cluster;
+ auto num_tx = provider.ConsumeIntegralInRange<ClusterIndex>(1, 32);
+ cluster.resize(num_tx);
+ for (ClusterIndex i = 0; i < num_tx; ++i) {
+ cluster[i].first.size = provider.ConsumeIntegralInRange<int32_t>(1, 0x3fffff);
+ cluster[i].first.fee = provider.ConsumeIntegralInRange<int64_t>(-0x8000000000000, 0x7ffffffffffff);
+ for (ClusterIndex j = 0; j < num_tx; ++j) {
+ if (i == j) continue;
+ if (provider.ConsumeBool()) cluster[i].second.Set(j);
+ }
+ }
+
+ // Construct dependency graph, and verify it matches the cluster (which includes a round-trip
+ // check for the serialization).
+ DepGraph depgraph(cluster);
+ VerifyDepGraphFromCluster(cluster, depgraph);
+}
+
+FUZZ_TARGET(clusterlin_depgraph_serialization)
+{
+ // Verify that any deserialized depgraph is acyclic and roundtrips to an identical depgraph.
+
+ // Construct a graph by deserializing.
+ SpanReader reader(buffer);
+ DepGraph<TestBitSet> depgraph;
+ try {
+ reader >> Using<DepGraphFormatter>(depgraph);
+ } catch (const std::ios_base::failure&) {}
+ SanityCheck(depgraph);
+
+ // Verify the graph is a DAG.
+ assert(IsAcyclic(depgraph));
+}
+
+FUZZ_TARGET(clusterlin_components)
+{
+ // Verify the behavior of DepGraphs's FindConnectedComponent and IsConnected functions.
+
+ // Construct a depgraph.
+ SpanReader reader(buffer);
+ DepGraph<TestBitSet> depgraph;
+ try {
+ reader >> Using<DepGraphFormatter>(depgraph);
+ } catch (const std::ios_base::failure&) {}
+
+ TestBitSet todo = TestBitSet::Fill(depgraph.TxCount());
+ while (todo.Any()) {
+ // Find a connected component inside todo.
+ auto component = depgraph.FindConnectedComponent(todo);
+
+ // The component must be a subset of todo and non-empty.
+ assert(component.IsSubsetOf(todo));
+ assert(component.Any());
+
+ // If todo is the entire graph, and the entire graph is connected, then the component must
+ // be the entire graph.
+ if (todo == TestBitSet::Fill(depgraph.TxCount())) {
+ assert((component == todo) == depgraph.IsConnected());
+ }
+
+ // If subset is connected, then component must match subset.
+ assert((component == todo) == depgraph.IsConnected(todo));
+
+ // The component cannot have any ancestors or descendants outside of component but in todo.
+ for (auto i : component) {
+ assert((depgraph.Ancestors(i) & todo).IsSubsetOf(component));
+ assert((depgraph.Descendants(i) & todo).IsSubsetOf(component));
+ }
+
+ // Starting from any component element, we must be able to reach every element.
+ for (auto i : component) {
+ // Start with just i as reachable.
+ TestBitSet reachable = TestBitSet::Singleton(i);
+ // Add in-todo descendants and ancestors to reachable until it does not change anymore.
+ while (true) {
+ TestBitSet new_reachable = reachable;
+ for (auto j : new_reachable) {
+ new_reachable |= depgraph.Ancestors(j) & todo;
+ new_reachable |= depgraph.Descendants(j) & todo;
+ }
+ if (new_reachable == reachable) break;
+ reachable = new_reachable;
+ }
+ // Verify that the result is the entire component.
+ assert(component == reachable);
+ }
+
+ // Construct an arbitrary subset of todo.
+ uint64_t subset_bits{0};
+ try {
+ reader >> VARINT(subset_bits);
+ } catch (const std::ios_base::failure&) {}
+ TestBitSet subset;
+ for (ClusterIndex i = 0; i < depgraph.TxCount(); ++i) {
+ if (todo[i]) {
+ if (subset_bits & 1) subset.Set(i);
+ subset_bits >>= 1;
+ }
+ }
+ // Which must be non-empty.
+ if (subset.None()) subset = TestBitSet::Singleton(todo.First());
+ // Remove it from todo.
+ todo -= subset;
+ }
+
+ // No components can be found in an empty subset.
+ assert(depgraph.FindConnectedComponent(todo).None());
+}
+
+FUZZ_TARGET(clusterlin_chunking)
+{
+ // Verify the correctness of the ChunkLinearization function.
+
+ // Construct a graph by deserializing.
+ SpanReader reader(buffer);
+ DepGraph<TestBitSet> depgraph;
+ try {
+ reader >> Using<DepGraphFormatter>(depgraph);
+ } catch (const std::ios_base::failure&) {}
+
+ // Read a valid linearization for depgraph.
+ auto linearization = ReadLinearization(depgraph, reader);
+
+ // Invoke the chunking function.
+ auto chunking = ChunkLinearization(depgraph, linearization);
+
+ // Verify that chunk feerates are monotonically non-increasing.
+ for (size_t i = 1; i < chunking.size(); ++i) {
+ assert(!(chunking[i] >> chunking[i - 1]));
+ }
+
+ // Naively recompute the chunks (each is the highest-feerate prefix of what remains).
+ auto todo = TestBitSet::Fill(depgraph.TxCount());
+ for (const auto& chunk_feerate : chunking) {
+ assert(todo.Any());
+ SetInfo<TestBitSet> accumulator, best;
+ for (ClusterIndex idx : linearization) {
+ if (todo[idx]) {
+ accumulator |= SetInfo(depgraph, idx);
+ if (best.feerate.IsEmpty() || accumulator.feerate >> best.feerate) {
+ best = accumulator;
+ }
+ }
+ }
+ assert(chunk_feerate == best.feerate);
+ assert(best.transactions.IsSubsetOf(todo));
+ todo -= best.transactions;
+ }
+ assert(todo.None());
+}
+
+FUZZ_TARGET(clusterlin_ancestor_finder)
+{
+ // Verify that AncestorCandidateFinder works as expected.
+
+ // Retrieve a depgraph from the fuzz input.
+ SpanReader reader(buffer);
+ DepGraph<TestBitSet> depgraph;
+ try {
+ reader >> Using<DepGraphFormatter>(depgraph);
+ } catch (const std::ios_base::failure&) {}
+
+ AncestorCandidateFinder anc_finder(depgraph);
+ auto todo = TestBitSet::Fill(depgraph.TxCount());
+ while (todo.Any()) {
+ // Call the ancestor finder's FindCandidateSet for what remains of the graph.
+ assert(!anc_finder.AllDone());
+ auto best_anc = anc_finder.FindCandidateSet();
+ // Sanity check the result.
+ assert(best_anc.transactions.Any());
+ assert(best_anc.transactions.IsSubsetOf(todo));
+ assert(depgraph.FeeRate(best_anc.transactions) == best_anc.feerate);
+ assert(depgraph.IsConnected(best_anc.transactions));
+ // Check that it is topologically valid.
+ for (auto i : best_anc.transactions) {
+ assert((depgraph.Ancestors(i) & todo).IsSubsetOf(best_anc.transactions));
+ }
+
+ // Compute all remaining ancestor sets.
+ std::optional<SetInfo<TestBitSet>> real_best_anc;
+ for (auto i : todo) {
+ SetInfo info(depgraph, todo & depgraph.Ancestors(i));
+ if (!real_best_anc.has_value() || info.feerate > real_best_anc->feerate) {
+ real_best_anc = info;
+ }
+ }
+ // The set returned by anc_finder must equal the real best ancestor sets.
+ assert(real_best_anc.has_value());
+ assert(*real_best_anc == best_anc);
+
+ // Find a topologically valid subset of transactions to remove from the graph.
+ auto del_set = ReadTopologicalSet(depgraph, todo, reader);
+ // If we did not find anything, use best_anc itself, because we should remove something.
+ if (del_set.None()) del_set = best_anc.transactions;
+ todo -= del_set;
+ anc_finder.MarkDone(del_set);
+ }
+ assert(anc_finder.AllDone());
+}
+
+static constexpr auto MAX_SIMPLE_ITERATIONS = 300000;
+
+FUZZ_TARGET(clusterlin_search_finder)
+{
+ // Verify that SearchCandidateFinder works as expected by sanity checking the results
+ // and comparing with the results from SimpleCandidateFinder, ExhaustiveCandidateFinder, and
+ // AncestorCandidateFinder.
+
+ // Retrieve an RNG seed and a depgraph from the fuzz input.
+ SpanReader reader(buffer);
+ DepGraph<TestBitSet> depgraph;
+ uint64_t rng_seed{0};
+ try {
+ reader >> Using<DepGraphFormatter>(depgraph) >> rng_seed;
+ } catch (const std::ios_base::failure&) {}
+
+ // Instantiate ALL the candidate finders.
+ SearchCandidateFinder src_finder(depgraph, rng_seed);
+ SimpleCandidateFinder smp_finder(depgraph);
+ ExhaustiveCandidateFinder exh_finder(depgraph);
+ AncestorCandidateFinder anc_finder(depgraph);
+
+ auto todo = TestBitSet::Fill(depgraph.TxCount());
+ while (todo.Any()) {
+ assert(!src_finder.AllDone());
+ assert(!smp_finder.AllDone());
+ assert(!exh_finder.AllDone());
+ assert(!anc_finder.AllDone());
+
+ // For each iteration, read an iteration count limit from the fuzz input.
+ uint64_t max_iterations = 1;
+ try {
+ reader >> VARINT(max_iterations);
+ } catch (const std::ios_base::failure&) {}
+ max_iterations &= 0xfffff;
+
+ // Read an initial subset from the fuzz input.
+ SetInfo init_best(depgraph, ReadTopologicalSet(depgraph, todo, reader));
+
+ // Call the search finder's FindCandidateSet for what remains of the graph.
+ auto [found, iterations_done] = src_finder.FindCandidateSet(max_iterations, init_best);
+
+ // Sanity check the result.
+ assert(iterations_done <= max_iterations);
+ assert(found.transactions.Any());
+ assert(found.transactions.IsSubsetOf(todo));
+ assert(depgraph.FeeRate(found.transactions) == found.feerate);
+ if (!init_best.feerate.IsEmpty()) assert(found.feerate >= init_best.feerate);
+ // Check that it is topologically valid.
+ for (auto i : found.transactions) {
+ assert(found.transactions.IsSupersetOf(depgraph.Ancestors(i) & todo));
+ }
+
+ // At most 2^N-1 iterations can be required: the number of non-empty subsets a graph with N
+ // transactions has.
+ assert(iterations_done <= ((uint64_t{1} << todo.Count()) - 1));
+
+ // Perform quality checks only if SearchCandidateFinder claims an optimal result.
+ if (iterations_done < max_iterations) {
+ // Optimal sets are always connected.
+ assert(depgraph.IsConnected(found.transactions));
+
+ // Compare with SimpleCandidateFinder.
+ auto [simple, simple_iters] = smp_finder.FindCandidateSet(MAX_SIMPLE_ITERATIONS);
+ assert(found.feerate >= simple.feerate);
+ if (simple_iters < MAX_SIMPLE_ITERATIONS) {
+ assert(found.feerate == simple.feerate);
+ }
+
+ // Compare with AncestorCandidateFinder;
+ auto anc = anc_finder.FindCandidateSet();
+ assert(found.feerate >= anc.feerate);
+
+ // Compare with ExhaustiveCandidateFinder. This quickly gets computationally expensive
+ // for large clusters (O(2^n)), so only do it for sufficiently small ones.
+ if (todo.Count() <= 12) {
+ auto exhaustive = exh_finder.FindCandidateSet();
+ assert(exhaustive.feerate == found.feerate);
+ // Also compare ExhaustiveCandidateFinder with SimpleCandidateFinder (this is
+ // primarily a test for SimpleCandidateFinder's correctness).
+ assert(exhaustive.feerate >= simple.feerate);
+ if (simple_iters < MAX_SIMPLE_ITERATIONS) {
+ assert(exhaustive.feerate == simple.feerate);
+ }
+ }
+ }
+
+ // Find a topologically valid subset of transactions to remove from the graph.
+ auto del_set = ReadTopologicalSet(depgraph, todo, reader);
+ // If we did not find anything, use found itself, because we should remove something.
+ if (del_set.None()) del_set = found.transactions;
+ todo -= del_set;
+ src_finder.MarkDone(del_set);
+ smp_finder.MarkDone(del_set);
+ exh_finder.MarkDone(del_set);
+ anc_finder.MarkDone(del_set);
+ }
+
+ assert(src_finder.AllDone());
+ assert(smp_finder.AllDone());
+ assert(exh_finder.AllDone());
+ assert(anc_finder.AllDone());
+}
+
+FUZZ_TARGET(clusterlin_linearization_chunking)
+{
+ // Verify the behavior of LinearizationChunking.
+
+ // Retrieve a depgraph from the fuzz input.
+ SpanReader reader(buffer);
+ DepGraph<TestBitSet> depgraph;
+ try {
+ reader >> Using<DepGraphFormatter>(depgraph);
+ } catch (const std::ios_base::failure&) {}
+
+ // Retrieve a topologically-valid subset of depgraph.
+ auto todo = TestBitSet::Fill(depgraph.TxCount());
+ auto subset = SetInfo(depgraph, ReadTopologicalSet(depgraph, todo, reader));
+
+ // Retrieve a valid linearization for depgraph.
+ auto linearization = ReadLinearization(depgraph, reader);
+
+ // Construct a LinearizationChunking object, initially for the whole linearization.
+ LinearizationChunking chunking(depgraph, linearization);
+
+ // Incrementally remove transactions from the chunking object, and check various properties at
+ // every step.
+ while (todo.Any()) {
+ assert(chunking.NumChunksLeft() > 0);
+
+ // Construct linearization with just todo.
+ std::vector<ClusterIndex> linearization_left;
+ for (auto i : linearization) {
+ if (todo[i]) linearization_left.push_back(i);
+ }
+
+ // Compute the chunking for linearization_left.
+ auto chunking_left = ChunkLinearization(depgraph, linearization_left);
+
+ // Verify that it matches the feerates of the chunks of chunking.
+ assert(chunking.NumChunksLeft() == chunking_left.size());
+ for (ClusterIndex i = 0; i < chunking.NumChunksLeft(); ++i) {
+ assert(chunking.GetChunk(i).feerate == chunking_left[i]);
+ }
+
+ // Check consistency of chunking.
+ TestBitSet combined;
+ for (ClusterIndex i = 0; i < chunking.NumChunksLeft(); ++i) {
+ const auto& chunk_info = chunking.GetChunk(i);
+ // Chunks must be non-empty.
+ assert(chunk_info.transactions.Any());
+ // Chunk feerates must be monotonically non-increasing.
+ if (i > 0) assert(!(chunk_info.feerate >> chunking.GetChunk(i - 1).feerate));
+ // Chunks must be a subset of what is left of the linearization.
+ assert(chunk_info.transactions.IsSubsetOf(todo));
+ // Chunks' claimed feerates must match their transactions' aggregate feerate.
+ assert(depgraph.FeeRate(chunk_info.transactions) == chunk_info.feerate);
+ // Chunks must be the highest-feerate remaining prefix.
+ SetInfo<TestBitSet> accumulator, best;
+ for (auto j : linearization) {
+ if (todo[j] && !combined[j]) {
+ accumulator |= SetInfo(depgraph, j);
+ if (best.feerate.IsEmpty() || accumulator.feerate > best.feerate) {
+ best = accumulator;
+ }
+ }
+ }
+ assert(best.transactions == chunk_info.transactions);
+ assert(best.feerate == chunk_info.feerate);
+ // Chunks cannot overlap.
+ assert(!chunk_info.transactions.Overlaps(combined));
+ combined |= chunk_info.transactions;
+ // Chunks must be topological.
+ for (auto idx : chunk_info.transactions) {
+ assert((depgraph.Ancestors(idx) & todo).IsSubsetOf(combined));
+ }
+ }
+ assert(combined == todo);
+
+ // Verify the expected properties of LinearizationChunking::IntersectPrefixes:
+ auto intersect = chunking.IntersectPrefixes(subset);
+ // - Intersecting again doesn't change the result.
+ assert(chunking.IntersectPrefixes(intersect) == intersect);
+ // - The intersection is topological.
+ TestBitSet intersect_anc;
+ for (auto idx : intersect.transactions) {
+ intersect_anc |= (depgraph.Ancestors(idx) & todo);
+ }
+ assert(intersect.transactions == intersect_anc);
+ // - The claimed intersection feerate matches its transactions.
+ assert(intersect.feerate == depgraph.FeeRate(intersect.transactions));
+ // - The intersection may only be empty if its input is empty.
+ assert(intersect.transactions.Any() == subset.transactions.Any());
+ // - The intersection feerate must be as high as the input.
+ assert(intersect.feerate >= subset.feerate);
+ // - No non-empty intersection between the intersection and a prefix of the chunks of the
+ // remainder of the linearization may be better than the intersection.
+ TestBitSet prefix;
+ for (ClusterIndex i = 0; i < chunking.NumChunksLeft(); ++i) {
+ prefix |= chunking.GetChunk(i).transactions;
+ auto reintersect = SetInfo(depgraph, prefix & intersect.transactions);
+ if (!reintersect.feerate.IsEmpty()) {
+ assert(reintersect.feerate <= intersect.feerate);
+ }
+ }
+
+ // Find a subset to remove from linearization.
+ auto done = ReadTopologicalSet(depgraph, todo, reader);
+ if (done.None()) {
+ // We need to remove a non-empty subset, so fall back to the unlinearized ancestors of
+ // the first transaction in todo if done is empty.
+ done = depgraph.Ancestors(todo.First()) & todo;
+ }
+ todo -= done;
+ chunking.MarkDone(done);
+ subset = SetInfo(depgraph, subset.transactions - done);
+ }
+
+ assert(chunking.NumChunksLeft() == 0);
+}
+
+FUZZ_TARGET(clusterlin_linearize)
+{
+ // Verify the behavior of Linearize().
+
+ // Retrieve an RNG seed, an iteration count, and a depgraph from the fuzz input.
+ SpanReader reader(buffer);
+ DepGraph<TestBitSet> depgraph;
+ uint64_t rng_seed{0};
+ uint64_t iter_count{0};
+ try {
+ reader >> VARINT(iter_count) >> Using<DepGraphFormatter>(depgraph) >> rng_seed;
+ } catch (const std::ios_base::failure&) {}
+
+ // Optionally construct an old linearization for it.
+ std::vector<ClusterIndex> old_linearization;
+ {
+ uint8_t have_old_linearization{0};
+ try {
+ reader >> have_old_linearization;
+ } catch(const std::ios_base::failure&) {}
+ if (have_old_linearization & 1) {
+ old_linearization = ReadLinearization(depgraph, reader);
+ SanityCheck(depgraph, old_linearization);
+ }
+ }
+
+ // Invoke Linearize().
+ iter_count &= 0x7ffff;
+ auto [linearization, optimal] = Linearize(depgraph, iter_count, rng_seed, old_linearization);
+ SanityCheck(depgraph, linearization);
+ auto chunking = ChunkLinearization(depgraph, linearization);
+
+ // Linearization must always be as good as the old one, if provided.
+ if (!old_linearization.empty()) {
+ auto old_chunking = ChunkLinearization(depgraph, old_linearization);
+ auto cmp = CompareChunks(chunking, old_chunking);
+ assert(cmp >= 0);
+ }
+
+ // If the iteration count is sufficiently high, an optimal linearization must be found.
+ // Each linearization step can use up to 2^k iterations, with steps k=1..n. That sum is
+ // 2 * (2^n - 1)
+ const uint64_t n = depgraph.TxCount();
+ if (n <= 18 && iter_count > 2U * ((uint64_t{1} << n) - 1U)) {
+ assert(optimal);
+ }
+
+ // If Linearize claims optimal result, run quality tests.
+ if (optimal) {
+ // It must be as good as SimpleLinearize.
+ auto [simple_linearization, simple_optimal] = SimpleLinearize(depgraph, MAX_SIMPLE_ITERATIONS);
+ SanityCheck(depgraph, simple_linearization);
+ auto simple_chunking = ChunkLinearization(depgraph, simple_linearization);
+ auto cmp = CompareChunks(chunking, simple_chunking);
+ assert(cmp >= 0);
+ // If SimpleLinearize finds the optimal result too, they must be equal (if not,
+ // SimpleLinearize is broken).
+ if (simple_optimal) assert(cmp == 0);
+
+ // Only for very small clusters, test every topologically-valid permutation.
+ if (depgraph.TxCount() <= 7) {
+ std::vector<ClusterIndex> perm_linearization(depgraph.TxCount());
+ for (ClusterIndex i = 0; i < depgraph.TxCount(); ++i) perm_linearization[i] = i;
+ // Iterate over all valid permutations.
+ do {
+ // Determine whether perm_linearization is topological.
+ TestBitSet perm_done;
+ bool perm_is_topo{true};
+ for (auto i : perm_linearization) {
+ perm_done.Set(i);
+ if (!depgraph.Ancestors(i).IsSubsetOf(perm_done)) {
+ perm_is_topo = false;
+ break;
+ }
+ }
+ // If so, verify that the obtained linearization is as good as the permutation.
+ if (perm_is_topo) {
+ auto perm_chunking = ChunkLinearization(depgraph, perm_linearization);
+ auto cmp = CompareChunks(chunking, perm_chunking);
+ assert(cmp >= 0);
+ }
+ } while(std::next_permutation(perm_linearization.begin(), perm_linearization.end()));
+ }
+ }
+}
+
+FUZZ_TARGET(clusterlin_postlinearize)
+{
+ // Verify expected properties of PostLinearize() on arbitrary linearizations.
+
+ // Retrieve a depgraph from the fuzz input.
+ SpanReader reader(buffer);
+ DepGraph<TestBitSet> depgraph;
+ try {
+ reader >> Using<DepGraphFormatter>(depgraph);
+ } catch (const std::ios_base::failure&) {}
+
+ // Retrieve a linearization from the fuzz input.
+ std::vector<ClusterIndex> linearization;
+ linearization = ReadLinearization(depgraph, reader);
+ SanityCheck(depgraph, linearization);
+
+ // Produce a post-processed version.
+ auto post_linearization = linearization;
+ PostLinearize(depgraph, post_linearization);
+ SanityCheck(depgraph, post_linearization);
+
+ // Compare diagrams: post-linearization cannot worsen anywhere.
+ auto chunking = ChunkLinearization(depgraph, linearization);
+ auto post_chunking = ChunkLinearization(depgraph, post_linearization);
+ auto cmp = CompareChunks(post_chunking, chunking);
+ assert(cmp >= 0);
+
+ // Run again, things can keep improving (and never get worse)
+ auto post_post_linearization = post_linearization;
+ PostLinearize(depgraph, post_post_linearization);
+ SanityCheck(depgraph, post_post_linearization);
+ auto post_post_chunking = ChunkLinearization(depgraph, post_post_linearization);
+ cmp = CompareChunks(post_post_chunking, post_chunking);
+ assert(cmp >= 0);
+
+ // The chunks that come out of postlinearizing are always connected.
+ LinearizationChunking linchunking(depgraph, post_linearization);
+ while (linchunking.NumChunksLeft()) {
+ assert(depgraph.IsConnected(linchunking.GetChunk(0).transactions));
+ linchunking.MarkDone(linchunking.GetChunk(0).transactions);
+ }
+}
+
+FUZZ_TARGET(clusterlin_postlinearize_tree)
+{
+ // Verify expected properties of PostLinearize() on linearizations of graphs that form either
+ // an upright or reverse tree structure.
+
+ // Construct a direction, RNG seed, and an arbitrary graph from the fuzz input.
+ SpanReader reader(buffer);
+ uint64_t rng_seed{0};
+ DepGraph<TestBitSet> depgraph_gen;
+ uint8_t direction{0};
+ try {
+ reader >> direction >> rng_seed >> Using<DepGraphFormatter>(depgraph_gen);
+ } catch (const std::ios_base::failure&) {}
+
+ // Now construct a new graph, copying the nodes, but leaving only the first parent (even
+ // direction) or the first child (odd direction).
+ DepGraph<TestBitSet> depgraph_tree;
+ for (ClusterIndex i = 0; i < depgraph_gen.TxCount(); ++i) {
+ depgraph_tree.AddTransaction(depgraph_gen.FeeRate(i));
+ }
+ if (direction & 1) {
+ for (ClusterIndex i = 0; i < depgraph_gen.TxCount(); ++i) {
+ auto children = depgraph_gen.Descendants(i) - TestBitSet::Singleton(i);
+ // Remove descendants that are children of other descendants.
+ for (auto j : children) {
+ if (!children[j]) continue;
+ children -= depgraph_gen.Descendants(j);
+ children.Set(j);
+ }
+ if (children.Any()) depgraph_tree.AddDependency(i, children.First());
+ }
+ } else {
+ for (ClusterIndex i = 0; i < depgraph_gen.TxCount(); ++i) {
+ auto parents = depgraph_gen.Ancestors(i) - TestBitSet::Singleton(i);
+ // Remove ancestors that are parents of other ancestors.
+ for (auto j : parents) {
+ if (!parents[j]) continue;
+ parents -= depgraph_gen.Ancestors(j);
+ parents.Set(j);
+ }
+ if (parents.Any()) depgraph_tree.AddDependency(parents.First(), i);
+ }
+ }
+
+ // Retrieve a linearization from the fuzz input.
+ std::vector<ClusterIndex> linearization;
+ linearization = ReadLinearization(depgraph_tree, reader);
+ SanityCheck(depgraph_tree, linearization);
+
+ // Produce a postlinearized version.
+ auto post_linearization = linearization;
+ PostLinearize(depgraph_tree, post_linearization);
+ SanityCheck(depgraph_tree, post_linearization);
+
+ // Compare diagrams.
+ auto chunking = ChunkLinearization(depgraph_tree, linearization);
+ auto post_chunking = ChunkLinearization(depgraph_tree, post_linearization);
+ auto cmp = CompareChunks(post_chunking, chunking);
+ assert(cmp >= 0);
+
+ // Verify that post-linearizing again does not change the diagram. The result must be identical
+ // as post_linearization ought to be optimal already with a tree-structured graph.
+ auto post_post_linearization = post_linearization;
+ PostLinearize(depgraph_tree, post_linearization);
+ SanityCheck(depgraph_tree, post_linearization);
+ auto post_post_chunking = ChunkLinearization(depgraph_tree, post_post_linearization);
+ auto cmp_post = CompareChunks(post_post_chunking, post_chunking);
+ assert(cmp_post == 0);
+
+ // Try to find an even better linearization directly. This must not change the diagram for the
+ // same reason.
+ auto [opt_linearization, _optimal] = Linearize(depgraph_tree, 100000, rng_seed, post_linearization);
+ auto opt_chunking = ChunkLinearization(depgraph_tree, opt_linearization);
+ auto cmp_opt = CompareChunks(opt_chunking, post_chunking);
+ assert(cmp_opt == 0);
+}
+
+FUZZ_TARGET(clusterlin_postlinearize_moved_leaf)
+{
+ // Verify that taking an existing linearization, and moving a leaf to the back, potentially
+ // increasing its fee, and then post-linearizing, results in something as good as the
+ // original. This guarantees that in an RBF that replaces a transaction with one of the same
+ // size but higher fee, applying the "remove conflicts, append new transaction, postlinearize"
+ // process will never worsen linearization quality.
+
+ // Construct an arbitrary graph and a fee from the fuzz input.
+ SpanReader reader(buffer);
+ DepGraph<TestBitSet> depgraph;
+ int32_t fee_inc{0};
+ try {
+ uint64_t fee_inc_code;
+ reader >> Using<DepGraphFormatter>(depgraph) >> VARINT(fee_inc_code);
+ fee_inc = fee_inc_code & 0x3ffff;
+ } catch (const std::ios_base::failure&) {}
+ if (depgraph.TxCount() == 0) return;
+
+ // Retrieve two linearizations from the fuzz input.
+ auto lin = ReadLinearization(depgraph, reader);
+ auto lin_leaf = ReadLinearization(depgraph, reader);
+
+ // Construct a linearization identical to lin, but with the tail end of lin_leaf moved to the
+ // back.
+ std::vector<ClusterIndex> lin_moved;
+ for (auto i : lin) {
+ if (i != lin_leaf.back()) lin_moved.push_back(i);
+ }
+ lin_moved.push_back(lin_leaf.back());
+
+ // Postlinearize lin_moved.
+ PostLinearize(depgraph, lin_moved);
+ SanityCheck(depgraph, lin_moved);
+
+ // Compare diagrams (applying the fee delta after computing the old one).
+ auto old_chunking = ChunkLinearization(depgraph, lin);
+ depgraph.FeeRate(lin_leaf.back()).fee += fee_inc;
+ auto new_chunking = ChunkLinearization(depgraph, lin_moved);
+ auto cmp = CompareChunks(new_chunking, old_chunking);
+ assert(cmp >= 0);
+}
+
+FUZZ_TARGET(clusterlin_merge)
+{
+ // Construct an arbitrary graph from the fuzz input.
+ SpanReader reader(buffer);
+ DepGraph<TestBitSet> depgraph;
+ try {
+ reader >> Using<DepGraphFormatter>(depgraph);
+ } catch (const std::ios_base::failure&) {}
+
+ // Retrieve two linearizations from the fuzz input.
+ auto lin1 = ReadLinearization(depgraph, reader);
+ auto lin2 = ReadLinearization(depgraph, reader);
+
+ // Merge the two.
+ auto lin_merged = MergeLinearizations(depgraph, lin1, lin2);
+
+ // Compute chunkings and compare.
+ auto chunking1 = ChunkLinearization(depgraph, lin1);
+ auto chunking2 = ChunkLinearization(depgraph, lin2);
+ auto chunking_merged = ChunkLinearization(depgraph, lin_merged);
+ auto cmp1 = CompareChunks(chunking_merged, chunking1);
+ assert(cmp1 >= 0);
+ auto cmp2 = CompareChunks(chunking_merged, chunking2);
+ assert(cmp2 >= 0);
+}
diff --git a/src/test/fuzz/coins_view.cpp b/src/test/fuzz/coins_view.cpp
index 8f3e357a84..368c69819a 100644
--- a/src/test/fuzz/coins_view.cpp
+++ b/src/test/fuzz/coins_view.cpp
@@ -27,7 +27,6 @@
#include <vector>
namespace {
-const TestingSetup* g_setup;
const Coin EMPTY_COIN{};
bool operator==(const Coin& a, const Coin& b)
@@ -39,8 +38,7 @@ bool operator==(const Coin& a, const Coin& b)
void initialize_coins_view()
{
- static const auto testing_setup = MakeNoLogFileContext<const TestingSetup>();
- g_setup = testing_setup.get();
+ static const auto testing_setup = MakeNoLogFileContext<>();
}
FUZZ_TARGET(coins_view, .init = initialize_coins_view)
@@ -122,12 +120,15 @@ FUZZ_TARGET(coins_view, .init = initialize_coins_view)
random_mutable_transaction = *opt_mutable_transaction;
},
[&] {
+ CoinsCachePair sentinel{};
+ sentinel.second.SelfRef(sentinel);
+ size_t usage{0};
CCoinsMapMemoryResource resource;
CCoinsMap coins_map{0, SaltedOutpointHasher{/*deterministic=*/true}, CCoinsMap::key_equal{}, &resource};
LIMITED_WHILE(good_data && fuzzed_data_provider.ConsumeBool(), 10'000)
{
CCoinsCacheEntry coins_cache_entry;
- coins_cache_entry.flags = fuzzed_data_provider.ConsumeIntegral<unsigned char>();
+ const auto flags{fuzzed_data_provider.ConsumeIntegral<uint8_t>()};
if (fuzzed_data_provider.ConsumeBool()) {
coins_cache_entry.coin = random_coin;
} else {
@@ -138,11 +139,14 @@ FUZZ_TARGET(coins_view, .init = initialize_coins_view)
}
coins_cache_entry.coin = *opt_coin;
}
- coins_map.emplace(random_out_point, std::move(coins_cache_entry));
+ auto it{coins_map.emplace(random_out_point, std::move(coins_cache_entry)).first};
+ it->second.AddFlags(flags, *it, sentinel);
+ usage += it->second.coin.DynamicMemoryUsage();
}
bool expected_code_path = false;
try {
- coins_view_cache.BatchWrite(coins_map, fuzzed_data_provider.ConsumeBool() ? ConsumeUInt256(fuzzed_data_provider) : coins_view_cache.GetBestBlock());
+ auto cursor{CoinsViewCacheCursor(usage, sentinel, coins_map, /*will_erase=*/true)};
+ coins_view_cache.BatchWrite(cursor, fuzzed_data_provider.ConsumeBool() ? ConsumeUInt256(fuzzed_data_provider) : coins_view_cache.GetBestBlock());
expected_code_path = true;
} catch (const std::logic_error& e) {
if (e.what() == std::string{"FRESH flag misapplied to coin that exists in parent cache"}) {
diff --git a/src/test/fuzz/coinscache_sim.cpp b/src/test/fuzz/coinscache_sim.cpp
index 648e96b4a0..8e717e96b4 100644
--- a/src/test/fuzz/coinscache_sim.cpp
+++ b/src/test/fuzz/coinscache_sim.cpp
@@ -172,13 +172,13 @@ public:
std::unique_ptr<CCoinsViewCursor> Cursor() const final { return {}; }
size_t EstimateSize() const final { return m_data.size(); }
- bool BatchWrite(CCoinsMap& data, const uint256&, bool erase) final
+ bool BatchWrite(CoinsViewCacheCursor& cursor, const uint256&) final
{
- for (auto it = data.begin(); it != data.end(); it = erase ? data.erase(it) : std::next(it)) {
- if (it->second.flags & CCoinsCacheEntry::DIRTY) {
+ for (auto it{cursor.Begin()}; it != cursor.End(); it = cursor.NextAndMaybeErase(*it)) {
+ if (it->second.IsDirty()) {
if (it->second.coin.IsSpent() && (it->first.n % 5) != 4) {
m_data.erase(it->first);
- } else if (erase) {
+ } else if (cursor.WillErase(*it)) {
m_data[it->first] = std::move(it->second.coin);
} else {
m_data[it->first] = it->second.coin;
diff --git a/src/test/fuzz/crypto_chacha20.cpp b/src/test/fuzz/crypto_chacha20.cpp
index 50c77bf699..d115a2b7e1 100644
--- a/src/test/fuzz/crypto_chacha20.cpp
+++ b/src/test/fuzz/crypto_chacha20.cpp
@@ -3,10 +3,10 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <crypto/chacha20.h>
+#include <random.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
-#include <test/util/xoroshiro128plusplus.h>
#include <array>
#include <cstddef>
@@ -53,7 +53,7 @@ namespace
once for a large block at once, and then the same data in chunks, comparing
the outcome.
- If UseCrypt, seeded Xoroshiro128++ output is used as input to Crypt().
+ If UseCrypt, seeded InsecureRandomContext output is used as input to Crypt().
If not, Keystream() is used directly, or sequences of 0x00 are encrypted.
*/
template<bool UseCrypt>
@@ -78,25 +78,11 @@ void ChaCha20SplitFuzz(FuzzedDataProvider& provider)
data1.resize(total_bytes);
data2.resize(total_bytes);
- // If using Crypt(), initialize data1 and data2 with the same Xoroshiro128++ based
+ // If using Crypt(), initialize data1 and data2 with the same InsecureRandomContext based
// stream.
if constexpr (UseCrypt) {
- uint64_t seed = provider.ConsumeIntegral<uint64_t>();
- XoRoShiRo128PlusPlus rng(seed);
- uint64_t bytes = 0;
- while (bytes < (total_bytes & ~uint64_t{7})) {
- uint64_t val = rng();
- WriteLE64(UCharCast(data1.data() + bytes), val);
- WriteLE64(UCharCast(data2.data() + bytes), val);
- bytes += 8;
- }
- if (bytes < total_bytes) {
- std::byte valbytes[8];
- uint64_t val = rng();
- WriteLE64(UCharCast(valbytes), val);
- std::copy(valbytes, valbytes + (total_bytes - bytes), data1.data() + bytes);
- std::copy(valbytes, valbytes + (total_bytes - bytes), data2.data() + bytes);
- }
+ InsecureRandomContext(provider.ConsumeIntegral<uint64_t>()).fillrand(data1);
+ std::copy(data1.begin(), data1.end(), data2.begin());
}
// Whether UseCrypt is used or not, the two byte arrays must match.
diff --git a/src/test/fuzz/crypto_chacha20poly1305.cpp b/src/test/fuzz/crypto_chacha20poly1305.cpp
new file mode 100644
index 0000000000..2b39a06094
--- /dev/null
+++ b/src/test/fuzz/crypto_chacha20poly1305.cpp
@@ -0,0 +1,200 @@
+// Copyright (c) 2020-2021 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <crypto/chacha20poly1305.h>
+#include <random.h>
+#include <span.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+
+#include <cstddef>
+#include <cstdint>
+#include <vector>
+
+constexpr static inline void crypt_till_rekey(FSChaCha20Poly1305& aead, int rekey_interval, bool encrypt)
+{
+ for (int i = 0; i < rekey_interval; ++i) {
+ std::byte dummy_tag[FSChaCha20Poly1305::EXPANSION] = {{}};
+ if (encrypt) {
+ aead.Encrypt(Span{dummy_tag}.first(0), Span{dummy_tag}.first(0), dummy_tag);
+ } else {
+ aead.Decrypt(dummy_tag, Span{dummy_tag}.first(0), Span{dummy_tag}.first(0));
+ }
+ }
+}
+
+FUZZ_TARGET(crypto_aeadchacha20poly1305)
+{
+ FuzzedDataProvider provider{buffer.data(), buffer.size()};
+
+ auto key = provider.ConsumeBytes<std::byte>(32);
+ key.resize(32);
+ AEADChaCha20Poly1305 aead(key);
+
+ // Initialize RNG deterministically, to generate contents and AAD. We assume that there are no
+ // (potentially buggy) edge cases triggered by specific values of contents/AAD, so we can avoid
+ // reading the actual data for those from the fuzzer input (which would need large amounts of
+ // data).
+ InsecureRandomContext rng(provider.ConsumeIntegral<uint64_t>());
+
+ LIMITED_WHILE(provider.ConsumeBool(), 10000)
+ {
+ // Mode:
+ // - Bit 0: whether to use single-plain Encrypt/Decrypt; otherwise use a split at prefix.
+ // - Bit 2: whether this ciphertext will be corrupted (making it the last sent one)
+ // - Bit 3-4: controls the maximum aad length (max 511 bytes)
+ // - Bit 5-7: controls the maximum content length (max 16383 bytes, for performance reasons)
+ unsigned mode = provider.ConsumeIntegral<uint8_t>();
+ bool use_splits = mode & 1;
+ bool damage = mode & 4;
+ unsigned aad_length_bits = 3 * ((mode >> 3) & 3);
+ unsigned aad_length = provider.ConsumeIntegralInRange<unsigned>(0, (1 << aad_length_bits) - 1);
+ unsigned length_bits = 2 * ((mode >> 5) & 7);
+ unsigned length = provider.ConsumeIntegralInRange<unsigned>(0, (1 << length_bits) - 1);
+ // Generate aad and content.
+ auto aad = rng.randbytes<std::byte>(aad_length);
+ auto plain = rng.randbytes<std::byte>(length);
+ std::vector<std::byte> cipher(length + AEADChaCha20Poly1305::EXPANSION);
+ // Generate nonce
+ AEADChaCha20Poly1305::Nonce96 nonce = {(uint32_t)rng(), rng()};
+
+ if (use_splits && length > 0) {
+ size_t split_index = provider.ConsumeIntegralInRange<size_t>(1, length);
+ aead.Encrypt(Span{plain}.first(split_index), Span{plain}.subspan(split_index), aad, nonce, cipher);
+ } else {
+ aead.Encrypt(plain, aad, nonce, cipher);
+ }
+
+ // Test Keystream output
+ std::vector<std::byte> keystream(length);
+ aead.Keystream(nonce, keystream);
+ for (size_t i = 0; i < length; ++i) {
+ assert((plain[i] ^ keystream[i]) == cipher[i]);
+ }
+
+ std::vector<std::byte> decrypted_contents(length);
+ bool ok{false};
+
+ // damage the key
+ unsigned key_position = provider.ConsumeIntegralInRange<unsigned>(0, 31);
+ std::byte damage_val{(uint8_t)(1U << (key_position & 7))};
+ std::vector<std::byte> bad_key = key;
+ bad_key[key_position] ^= damage_val;
+
+ AEADChaCha20Poly1305 bad_aead(bad_key);
+ ok = bad_aead.Decrypt(cipher, aad, nonce, decrypted_contents);
+ assert(!ok);
+
+ // Optionally damage 1 bit in either the cipher (corresponding to a change in transit)
+ // or the aad (to make sure that decryption will fail if the AAD mismatches).
+ if (damage) {
+ unsigned damage_bit = provider.ConsumeIntegralInRange<unsigned>(0, (cipher.size() + aad.size()) * 8U - 1U);
+ unsigned damage_pos = damage_bit >> 3;
+ std::byte damage_val{(uint8_t)(1U << (damage_bit & 7))};
+ if (damage_pos >= cipher.size()) {
+ aad[damage_pos - cipher.size()] ^= damage_val;
+ } else {
+ cipher[damage_pos] ^= damage_val;
+ }
+ }
+
+ if (use_splits && length > 0) {
+ size_t split_index = provider.ConsumeIntegralInRange<size_t>(1, length);
+ ok = aead.Decrypt(cipher, aad, nonce, Span{decrypted_contents}.first(split_index), Span{decrypted_contents}.subspan(split_index));
+ } else {
+ ok = aead.Decrypt(cipher, aad, nonce, decrypted_contents);
+ }
+
+ // Decryption *must* fail if the packet was damaged, and succeed if it wasn't.
+ assert(!ok == damage);
+ if (!ok) break;
+ assert(decrypted_contents == plain);
+ }
+}
+
+FUZZ_TARGET(crypto_fschacha20poly1305)
+{
+ FuzzedDataProvider provider{buffer.data(), buffer.size()};
+
+ uint32_t rekey_interval = provider.ConsumeIntegralInRange<size_t>(32, 512);
+ auto key = provider.ConsumeBytes<std::byte>(32);
+ key.resize(32);
+ FSChaCha20Poly1305 enc_aead(key, rekey_interval);
+ FSChaCha20Poly1305 dec_aead(key, rekey_interval);
+
+ // Initialize RNG deterministically, to generate contents and AAD. We assume that there are no
+ // (potentially buggy) edge cases triggered by specific values of contents/AAD, so we can avoid
+ // reading the actual data for those from the fuzzer input (which would need large amounts of
+ // data).
+ InsecureRandomContext rng(provider.ConsumeIntegral<uint64_t>());
+
+ LIMITED_WHILE(provider.ConsumeBool(), 10000)
+ {
+ // Mode:
+ // - Bit 0: whether to use single-plain Encrypt/Decrypt; otherwise use a split at prefix.
+ // - Bit 2: whether this ciphertext will be corrupted (making it the last sent one)
+ // - Bit 3-4: controls the maximum aad length (max 511 bytes)
+ // - Bit 5-7: controls the maximum content length (max 16383 bytes, for performance reasons)
+ unsigned mode = provider.ConsumeIntegral<uint8_t>();
+ bool use_splits = mode & 1;
+ bool damage = mode & 4;
+ unsigned aad_length_bits = 3 * ((mode >> 3) & 3);
+ unsigned aad_length = provider.ConsumeIntegralInRange<unsigned>(0, (1 << aad_length_bits) - 1);
+ unsigned length_bits = 2 * ((mode >> 5) & 7);
+ unsigned length = provider.ConsumeIntegralInRange<unsigned>(0, (1 << length_bits) - 1);
+ // Generate aad and content.
+ auto aad = rng.randbytes<std::byte>(aad_length);
+ auto plain = rng.randbytes<std::byte>(length);
+ std::vector<std::byte> cipher(length + FSChaCha20Poly1305::EXPANSION);
+
+ crypt_till_rekey(enc_aead, rekey_interval, true);
+ if (use_splits && length > 0) {
+ size_t split_index = provider.ConsumeIntegralInRange<size_t>(1, length);
+ enc_aead.Encrypt(Span{plain}.first(split_index), Span{plain}.subspan(split_index), aad, cipher);
+ } else {
+ enc_aead.Encrypt(plain, aad, cipher);
+ }
+
+ std::vector<std::byte> decrypted_contents(length);
+ bool ok{false};
+
+ // damage the key
+ unsigned key_position = provider.ConsumeIntegralInRange<unsigned>(0, 31);
+ std::byte damage_val{(uint8_t)(1U << (key_position & 7))};
+ std::vector<std::byte> bad_key = key;
+ bad_key[key_position] ^= damage_val;
+
+ FSChaCha20Poly1305 bad_fs_aead(bad_key, rekey_interval);
+ crypt_till_rekey(bad_fs_aead, rekey_interval, false);
+ ok = bad_fs_aead.Decrypt(cipher, aad, decrypted_contents);
+ assert(!ok);
+
+ // Optionally damage 1 bit in either the cipher (corresponding to a change in transit)
+ // or the aad (to make sure that decryption will fail if the AAD mismatches).
+ if (damage) {
+ unsigned damage_bit = provider.ConsumeIntegralInRange<unsigned>(0, (cipher.size() + aad.size()) * 8U - 1U);
+ unsigned damage_pos = damage_bit >> 3;
+ std::byte damage_val{(uint8_t)(1U << (damage_bit & 7))};
+ if (damage_pos >= cipher.size()) {
+ aad[damage_pos - cipher.size()] ^= damage_val;
+ } else {
+ cipher[damage_pos] ^= damage_val;
+ }
+ }
+
+ crypt_till_rekey(dec_aead, rekey_interval, false);
+ if (use_splits && length > 0) {
+ size_t split_index = provider.ConsumeIntegralInRange<size_t>(1, length);
+ ok = dec_aead.Decrypt(cipher, aad, Span{decrypted_contents}.first(split_index), Span{decrypted_contents}.subspan(split_index));
+ } else {
+ ok = dec_aead.Decrypt(cipher, aad, decrypted_contents);
+ }
+
+ // Decryption *must* fail if the packet was damaged, and succeed if it wasn't.
+ assert(!ok == damage);
+ if (!ok) break;
+ assert(decrypted_contents == plain);
+ }
+}
diff --git a/src/test/fuzz/descriptor_parse.cpp b/src/test/fuzz/descriptor_parse.cpp
index b9a5560ffb..6a3f4d6dfe 100644
--- a/src/test/fuzz/descriptor_parse.cpp
+++ b/src/test/fuzz/descriptor_parse.cpp
@@ -72,6 +72,14 @@ FUZZ_TARGET(mocked_descriptor_parse, .init = initialize_mocked_descriptor_parse)
// out strings which could correspond to a descriptor containing a too large derivation path.
if (HasDeepDerivPath(buffer)) return;
+ // Some fragments can take a virtually unlimited number of sub-fragments (thresh, multi_a) but
+ // may perform quadratic operations on them. Limit the number of sub-fragments per fragment.
+ if (HasTooManySubFrag(buffer)) return;
+
+ // The script building logic performs quadratic copies in the number of nested wrappers. Limit
+ // the number of nested wrappers per fragment.
+ if (HasTooManyWrappers(buffer)) return;
+
const std::string mocked_descriptor{buffer.begin(), buffer.end()};
if (const auto descriptor = MOCKED_DESC_CONVERTER.GetDescriptor(mocked_descriptor)) {
FlatSigningProvider signing_provider;
@@ -83,8 +91,10 @@ FUZZ_TARGET(mocked_descriptor_parse, .init = initialize_mocked_descriptor_parse)
FUZZ_TARGET(descriptor_parse, .init = initialize_descriptor_parse)
{
- // See comment above for rationale.
+ // See comments above for rationales.
if (HasDeepDerivPath(buffer)) return;
+ if (HasTooManySubFrag(buffer)) return;
+ if (HasTooManyWrappers(buffer)) return;
const std::string descriptor(buffer.begin(), buffer.end());
FlatSigningProvider signing_provider;
diff --git a/src/test/fuzz/fuzz.cpp b/src/test/fuzz/fuzz.cpp
index c1c9945a04..96283a3e15 100644
--- a/src/test/fuzz/fuzz.cpp
+++ b/src/test/fuzz/fuzz.cpp
@@ -6,6 +6,7 @@
#include <netaddress.h>
#include <netbase.h>
+#include <test/util/random.h>
#include <test/util/setup_common.h>
#include <util/check.h>
#include <util/fs.h>
@@ -72,8 +73,8 @@ auto& FuzzTargets()
void FuzzFrameworkRegisterTarget(std::string_view name, TypeTestOneInput target, FuzzTargetOptions opts)
{
- const auto it_ins{FuzzTargets().try_emplace(name, FuzzTarget /* temporary can be dropped after clang-16 */ {std::move(target), std::move(opts)})};
- Assert(it_ins.second);
+ const auto [it, ins]{FuzzTargets().try_emplace(name, FuzzTarget /* temporary can be dropped after Apple-Clang-16 ? */ {std::move(target), std::move(opts)})};
+ Assert(ins);
}
static std::string_view g_fuzz_target;
@@ -101,6 +102,12 @@ void ResetCoverageCounters() {}
void initialize()
{
+ // By default, make the RNG deterministic with a fixed seed. This will affect all
+ // randomness during the fuzz test, except:
+ // - GetStrongRandBytes(), which is used for the creation of private key material.
+ // - Creating a BasicTestingSetup or derived class will switch to a random seed.
+ SeedRandomForTest(SeedRand::ZEROS);
+
// Terminate immediately if a fuzzing harness ever tries to create a socket.
// Individual tests can override this by pointing CreateSock to a mocked alternative.
CreateSock = [](int, int, int) -> std::unique_ptr<Sock> { std::terminate(); };
diff --git a/src/test/fuzz/hex.cpp b/src/test/fuzz/hex.cpp
index f67b820d11..ebe30c3c1a 100644
--- a/src/test/fuzz/hex.cpp
+++ b/src/test/fuzz/hex.cpp
@@ -10,6 +10,7 @@
#include <uint256.h>
#include <univalue.h>
#include <util/strencodings.h>
+#include <util/transaction_identifier.h>
#include <cassert>
#include <cstdint>
@@ -27,8 +28,11 @@ FUZZ_TARGET(hex)
assert(ToLower(random_hex_string) == hex_data);
}
(void)IsHexNumber(random_hex_string);
- uint256 result;
- (void)ParseHashStr(random_hex_string, result);
+ if (uint256::FromHex(random_hex_string)) {
+ assert(random_hex_string.length() == 64);
+ assert(Txid::FromHex(random_hex_string));
+ assert(Wtxid::FromHex(random_hex_string));
+ }
(void)uint256S(random_hex_string);
try {
(void)HexToPubKey(random_hex_string);
diff --git a/src/test/fuzz/integer.cpp b/src/test/fuzz/integer.cpp
index 8f1d7b6d45..02c6796d11 100644
--- a/src/test/fuzz/integer.cpp
+++ b/src/test/fuzz/integer.cpp
@@ -78,8 +78,8 @@ FUZZ_TARGET(integer, .init = initialize_integer)
} else {
(void)CompressAmount(u64);
}
- static const uint256 u256_min(uint256S("0000000000000000000000000000000000000000000000000000000000000000"));
- static const uint256 u256_max(uint256S("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"));
+ constexpr uint256 u256_min{"0000000000000000000000000000000000000000000000000000000000000000"};
+ constexpr uint256 u256_max{"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"};
const std::vector<uint256> v256{u256, u256_min, u256_max};
(void)ComputeMerkleRoot(v256);
(void)DecompressAmount(u64);
diff --git a/src/test/fuzz/kitchen_sink.cpp b/src/test/fuzz/kitchen_sink.cpp
index 4468f358d9..62b49106cd 100644
--- a/src/test/fuzz/kitchen_sink.cpp
+++ b/src/test/fuzz/kitchen_sink.cpp
@@ -23,7 +23,7 @@ using node::TransactionError;
namespace {
constexpr TransactionError ALL_TRANSACTION_ERROR[] = {
TransactionError::MISSING_INPUTS,
- TransactionError::ALREADY_IN_CHAIN,
+ TransactionError::ALREADY_IN_UTXO_SET,
TransactionError::MEMPOOL_REJECTED,
TransactionError::MEMPOOL_ERROR,
TransactionError::MAX_FEE_EXCEEDED,
diff --git a/src/test/fuzz/mini_miner.cpp b/src/test/fuzz/mini_miner.cpp
index 3a1663364f..51de4d0166 100644
--- a/src/test/fuzz/mini_miner.cpp
+++ b/src/test/fuzz/mini_miner.cpp
@@ -188,9 +188,9 @@ FUZZ_TARGET(mini_miner_selection, .init = initialize_miner)
auto mock_template_txids = mini_miner.GetMockTemplateTxids();
// MiniMiner doesn't add a coinbase tx.
assert(mock_template_txids.count(blocktemplate->block.vtx[0]->GetHash()) == 0);
- mock_template_txids.emplace(blocktemplate->block.vtx[0]->GetHash());
- assert(mock_template_txids.size() <= blocktemplate->block.vtx.size());
- assert(mock_template_txids.size() >= blocktemplate->block.vtx.size());
+ auto [iter, new_entry] = mock_template_txids.emplace(blocktemplate->block.vtx[0]->GetHash());
+ assert(new_entry);
+
assert(mock_template_txids.size() == blocktemplate->block.vtx.size());
for (const auto& tx : blocktemplate->block.vtx) {
assert(mock_template_txids.count(tx->GetHash()));
diff --git a/src/test/fuzz/miniscript.cpp b/src/test/fuzz/miniscript.cpp
index 7e71af7c44..1f9ed9a064 100644
--- a/src/test/fuzz/miniscript.cpp
+++ b/src/test/fuzz/miniscript.cpp
@@ -47,9 +47,9 @@ struct TestData {
void Init() {
unsigned char keydata[32] = {1};
// All our signatures sign (and are required to sign) this constant message.
- auto const MESSAGE_HASH{uint256S("f5cd94e18b6fe77dd7aca9e35c2b0c9cbd86356c80a71065")};
+ constexpr uint256 MESSAGE_HASH{"0000000000000000f5cd94e18b6fe77dd7aca9e35c2b0c9cbd86356c80a71065"};
// We don't pass additional randomness when creating a schnorr signature.
- auto const EMPTY_AUX{uint256S("")};
+ const auto EMPTY_AUX{uint256::ZERO};
for (size_t i = 0; i < 256; i++) {
keydata[31] = i;
diff --git a/src/test/fuzz/muhash.cpp b/src/test/fuzz/muhash.cpp
index dd34c465ed..e74bcb962f 100644
--- a/src/test/fuzz/muhash.cpp
+++ b/src/test/fuzz/muhash.cpp
@@ -20,7 +20,7 @@ FUZZ_TARGET(muhash)
muhash.Insert(data);
muhash.Insert(data2);
- const std::string initial_state_hash{"dd5ad2a105c2d29495f577245c357409002329b9f4d6182c0af3dc2f462555c8"};
+ constexpr uint256 initial_state_hash{"dd5ad2a105c2d29495f577245c357409002329b9f4d6182c0af3dc2f462555c8"};
uint256 out;
uint256 out2;
CallOneOf(
@@ -57,14 +57,14 @@ FUZZ_TARGET(muhash)
#endif
muhash.Finalize(out);
- out2 = uint256S(initial_state_hash);
+ out2 = initial_state_hash;
},
[&] {
// Test that removing all added elements brings the object back to it's initial state
muhash.Remove(data);
muhash.Remove(data2);
muhash.Finalize(out);
- out2 = uint256S(initial_state_hash);
+ out2 = initial_state_hash;
});
assert(out == out2);
}
diff --git a/src/test/fuzz/net.cpp b/src/test/fuzz/net.cpp
index e8b1480c5b..4cec96274e 100644
--- a/src/test/fuzz/net.cpp
+++ b/src/test/fuzz/net.cpp
@@ -33,6 +33,11 @@ FUZZ_TARGET(net, .init = initialize_net)
SetMockTime(ConsumeTime(fuzzed_data_provider));
CNode node{ConsumeNode(fuzzed_data_provider)};
node.SetCommonVersion(fuzzed_data_provider.ConsumeIntegral<int>());
+ if (const auto service_opt =
+ ConsumeDeserializable<CService>(fuzzed_data_provider, ConsumeDeserializationParams<CNetAddr::SerParams>(fuzzed_data_provider)))
+ {
+ node.SetAddrLocal(*service_opt);
+ }
LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 10000) {
CallOneOf(
fuzzed_data_provider,
@@ -53,13 +58,6 @@ FUZZ_TARGET(net, .init = initialize_net)
}
},
[&] {
- const std::optional<CService> service_opt = ConsumeDeserializable<CService>(fuzzed_data_provider, ConsumeDeserializationParams<CNetAddr::SerParams>(fuzzed_data_provider));
- if (!service_opt) {
- return;
- }
- node.SetAddrLocal(*service_opt);
- },
- [&] {
const std::vector<uint8_t> b = ConsumeRandomLengthByteVector(fuzzed_data_provider);
bool complete;
node.ReceiveMsgBytes(b, complete);
diff --git a/src/test/fuzz/p2p_handshake.cpp b/src/test/fuzz/p2p_handshake.cpp
new file mode 100644
index 0000000000..217655ab70
--- /dev/null
+++ b/src/test/fuzz/p2p_handshake.cpp
@@ -0,0 +1,107 @@
+// Copyright (c) 2020-present 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 <addrman.h>
+#include <consensus/consensus.h>
+#include <net.h>
+#include <net_processing.h>
+#include <node/warnings.h>
+#include <protocol.h>
+#include <script/script.h>
+#include <sync.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/mining.h>
+#include <test/util/net.h>
+#include <test/util/setup_common.h>
+#include <test/util/validation.h>
+#include <util/time.h>
+#include <validationinterface.h>
+
+#include <ios>
+#include <string>
+#include <utility>
+#include <vector>
+
+namespace {
+const TestingSetup* g_setup;
+
+void initialize()
+{
+ static const auto testing_setup = MakeNoLogFileContext<const TestingSetup>(
+ /*chain_type=*/ChainType::REGTEST);
+ g_setup = testing_setup.get();
+}
+} // namespace
+
+FUZZ_TARGET(p2p_handshake, .init = ::initialize)
+{
+ FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
+
+ ConnmanTestMsg& connman = static_cast<ConnmanTestMsg&>(*g_setup->m_node.connman);
+ auto& chainman = static_cast<TestChainstateManager&>(*g_setup->m_node.chainman);
+ SetMockTime(1610000000); // any time to successfully reset ibd
+ chainman.ResetIbd();
+
+ node::Warnings warnings{};
+ NetGroupManager netgroupman{{}};
+ AddrMan addrman{netgroupman, /*deterministic=*/true, 0};
+ auto peerman = PeerManager::make(connman, addrman,
+ /*banman=*/nullptr, chainman,
+ *g_setup->m_node.mempool, warnings,
+ PeerManager::Options{
+ .reconcile_txs = true,
+ .deterministic_rng = true,
+ });
+ connman.SetMsgProc(peerman.get());
+
+ LOCK(NetEventsInterface::g_msgproc_mutex);
+
+ std::vector<CNode*> peers;
+ const auto num_peers_to_add = fuzzed_data_provider.ConsumeIntegralInRange(1, 3);
+ for (int i = 0; i < num_peers_to_add; ++i) {
+ peers.push_back(ConsumeNodeAsUniquePtr(fuzzed_data_provider, i).release());
+ connman.AddTestNode(*peers.back());
+ peerman->InitializeNode(
+ *peers.back(),
+ static_cast<ServiceFlags>(fuzzed_data_provider.ConsumeIntegral<uint64_t>()));
+ }
+
+ LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 100)
+ {
+ CNode& connection = *PickValue(fuzzed_data_provider, peers);
+ if (connection.fDisconnect || connection.fSuccessfullyConnected) {
+ // Skip if the the connection was disconnected or if the version
+ // handshake was already completed.
+ continue;
+ }
+
+ SetMockTime(GetTime() +
+ fuzzed_data_provider.ConsumeIntegralInRange<int64_t>(
+ -std::chrono::seconds{10min}.count(), // Allow mocktime to go backwards slightly
+ std::chrono::seconds{TIMEOUT_INTERVAL}.count()));
+
+ CSerializedNetMsg net_msg;
+ net_msg.m_type = PickValue(fuzzed_data_provider, ALL_NET_MESSAGE_TYPES);
+ net_msg.data = ConsumeRandomLengthByteVector(fuzzed_data_provider, MAX_PROTOCOL_MESSAGE_LENGTH);
+
+ connman.FlushSendBuffer(connection);
+ (void)connman.ReceiveMsgFrom(connection, std::move(net_msg));
+
+ bool more_work{true};
+ while (more_work) {
+ connection.fPauseSend = false;
+
+ try {
+ more_work = connman.ProcessMessagesOnce(connection);
+ } catch (const std::ios_base::failure&) {
+ }
+ peerman->SendMessages(&connection);
+ }
+ }
+
+ g_setup->m_node.connman->StopNodes();
+}
diff --git a/src/test/fuzz/p2p_transport_serialization.cpp b/src/test/fuzz/p2p_transport_serialization.cpp
index 767238d103..93f77b6e5b 100644
--- a/src/test/fuzz/p2p_transport_serialization.cpp
+++ b/src/test/fuzz/p2p_transport_serialization.cpp
@@ -10,7 +10,6 @@
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
-#include <test/util/xoroshiro128plusplus.h>
#include <util/chaintype.h>
#include <cassert>
@@ -104,7 +103,7 @@ FUZZ_TARGET(p2p_transport_serialization, .init = initialize_p2p_transport_serial
namespace {
-template<typename R>
+template<RandomNumberGenerator R>
void SimulationTest(Transport& initiator, Transport& responder, R& rng, FuzzedDataProvider& provider)
{
// Simulation test with two Transport objects, which send messages to each other, with
@@ -165,8 +164,7 @@ void SimulationTest(Transport& initiator, Transport& responder, R& rng, FuzzedDa
// Determine size of message to send (limited to 75 kB for performance reasons).
size_t size = provider.ConsumeIntegralInRange<uint32_t>(0, 75000);
// Get payload of message from RNG.
- msg.data.resize(size);
- for (auto& v : msg.data) v = uint8_t(rng());
+ msg.data = rng.randbytes(size);
// Return.
return msg;
};
@@ -337,7 +335,7 @@ std::unique_ptr<Transport> MakeV1Transport(NodeId nodeid) noexcept
return std::make_unique<V1Transport>(nodeid);
}
-template<typename RNG>
+template<RandomNumberGenerator RNG>
std::unique_ptr<Transport> MakeV2Transport(NodeId nodeid, bool initiator, RNG& rng, FuzzedDataProvider& provider)
{
// Retrieve key
@@ -353,8 +351,7 @@ std::unique_ptr<Transport> MakeV2Transport(NodeId nodeid, bool initiator, RNG& r
} else {
// If it's longer, generate it from the RNG. This avoids having large amounts of
// (hopefully) irrelevant data needing to be stored in the fuzzer data.
- garb.resize(garb_len);
- for (auto& v : garb) v = uint8_t(rng());
+ garb = rng.randbytes(garb_len);
}
// Retrieve entropy
auto ent = provider.ConsumeBytes<std::byte>(32);
@@ -378,7 +375,7 @@ FUZZ_TARGET(p2p_transport_bidirectional, .init = initialize_p2p_transport_serial
{
// Test with two V1 transports talking to each other.
FuzzedDataProvider provider{buffer.data(), buffer.size()};
- XoRoShiRo128PlusPlus rng(provider.ConsumeIntegral<uint64_t>());
+ InsecureRandomContext rng(provider.ConsumeIntegral<uint64_t>());
auto t1 = MakeV1Transport(NodeId{0});
auto t2 = MakeV1Transport(NodeId{1});
if (!t1 || !t2) return;
@@ -389,7 +386,7 @@ FUZZ_TARGET(p2p_transport_bidirectional_v2, .init = initialize_p2p_transport_ser
{
// Test with two V2 transports talking to each other.
FuzzedDataProvider provider{buffer.data(), buffer.size()};
- XoRoShiRo128PlusPlus rng(provider.ConsumeIntegral<uint64_t>());
+ InsecureRandomContext rng(provider.ConsumeIntegral<uint64_t>());
auto t1 = MakeV2Transport(NodeId{0}, true, rng, provider);
auto t2 = MakeV2Transport(NodeId{1}, false, rng, provider);
if (!t1 || !t2) return;
@@ -400,7 +397,7 @@ FUZZ_TARGET(p2p_transport_bidirectional_v1v2, .init = initialize_p2p_transport_s
{
// Test with a V1 initiator talking to a V2 responder.
FuzzedDataProvider provider{buffer.data(), buffer.size()};
- XoRoShiRo128PlusPlus rng(provider.ConsumeIntegral<uint64_t>());
+ InsecureRandomContext rng(provider.ConsumeIntegral<uint64_t>());
auto t1 = MakeV1Transport(NodeId{0});
auto t2 = MakeV2Transport(NodeId{1}, false, rng, provider);
if (!t1 || !t2) return;
diff --git a/src/test/fuzz/parse_univalue.cpp b/src/test/fuzz/parse_univalue.cpp
index a3d6ab6375..cb39b3be83 100644
--- a/src/test/fuzz/parse_univalue.cpp
+++ b/src/test/fuzz/parse_univalue.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2009-2022 The Bitcoin Core developers
+// Copyright (c) 2009-present The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -77,7 +77,7 @@ FUZZ_TARGET(parse_univalue, .init = initialize_parse_univalue)
}
try {
FlatSigningProvider provider;
- (void)EvalDescriptorStringOrObject(univalue, provider);
+ if (buffer.size() < 10'000) (void)EvalDescriptorStringOrObject(univalue, provider);
} catch (const UniValue&) {
} catch (const std::runtime_error&) {
}
diff --git a/src/test/fuzz/poolresource.cpp b/src/test/fuzz/poolresource.cpp
index f764d9f8db..28bf7175c0 100644
--- a/src/test/fuzz/poolresource.cpp
+++ b/src/test/fuzz/poolresource.cpp
@@ -2,13 +2,13 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <random.h>
#include <span.h>
#include <support/allocators/pool.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
#include <test/util/poolresourcetester.h>
-#include <test/util/xoroshiro128plusplus.h>
#include <cstdint>
#include <tuple>
@@ -71,41 +71,14 @@ public:
void RandomContentFill(Entry& entry)
{
- XoRoShiRo128PlusPlus rng(entry.seed);
- auto ptr = entry.span.data();
- auto size = entry.span.size();
-
- while (size >= 8) {
- auto r = rng();
- std::memcpy(ptr, &r, 8);
- size -= 8;
- ptr += 8;
- }
- if (size > 0) {
- auto r = rng();
- std::memcpy(ptr, &r, size);
- }
+ InsecureRandomContext(entry.seed).fillrand(entry.span);
}
void RandomContentCheck(const Entry& entry)
{
- XoRoShiRo128PlusPlus rng(entry.seed);
- auto ptr = entry.span.data();
- auto size = entry.span.size();
-
- std::byte buf[8];
- while (size >= 8) {
- auto r = rng();
- std::memcpy(buf, &r, 8);
- assert(std::memcmp(buf, ptr, 8) == 0);
- size -= 8;
- ptr += 8;
- }
- if (size > 0) {
- auto r = rng();
- std::memcpy(buf, &r, size);
- assert(std::memcmp(buf, ptr, size) == 0);
- }
+ std::vector<std::byte> expect(entry.span.size());
+ InsecureRandomContext(entry.seed).fillrand(expect);
+ assert(entry.span == expect);
}
void Deallocate(const Entry& entry)
diff --git a/src/test/fuzz/prevector.cpp b/src/test/fuzz/prevector.cpp
index 9cea32e304..aeceb38a58 100644
--- a/src/test/fuzz/prevector.cpp
+++ b/src/test/fuzz/prevector.cpp
@@ -2,16 +2,14 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <test/fuzz/FuzzedDataProvider.h>
-#include <test/fuzz/fuzz.h>
-
#include <prevector.h>
-#include <vector>
-
-#include <reverse_iterator.h>
#include <serialize.h>
#include <streams.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <ranges>
+#include <vector>
namespace {
template <unsigned int N, typename T>
@@ -47,7 +45,7 @@ public:
assert(v == real_vector[pos]);
++pos;
}
- for (const T& v : reverse_iterate(pre_vector)) {
+ for (const T& v : pre_vector | std::views::reverse) {
--pos;
assert(v == real_vector[pos]);
}
@@ -55,7 +53,7 @@ public:
assert(v == real_vector[pos]);
++pos;
}
- for (const T& v : reverse_iterate(const_pre_vector)) {
+ for (const T& v : const_pre_vector | std::views::reverse) {
--pos;
assert(v == real_vector[pos]);
}
diff --git a/src/test/fuzz/process_message.cpp b/src/test/fuzz/process_message.cpp
index d10d9dafe8..6373eac1c3 100644
--- a/src/test/fuzz/process_message.cpp
+++ b/src/test/fuzz/process_message.cpp
@@ -42,7 +42,7 @@ void initialize_process_message()
static const auto testing_setup = MakeNoLogFileContext<const TestingSetup>(
/*chain_type=*/ChainType::REGTEST,
- /*extra_args=*/{"-txreconciliation"});
+ {.extra_args = {"-txreconciliation"}});
g_setup = testing_setup.get();
for (int i = 0; i < 2 * COINBASE_MATURITY; i++) {
MineBlock(g_setup->m_node, CScript() << OP_TRUE);
diff --git a/src/test/fuzz/process_messages.cpp b/src/test/fuzz/process_messages.cpp
index 38acd432fa..62f38967a3 100644
--- a/src/test/fuzz/process_messages.cpp
+++ b/src/test/fuzz/process_messages.cpp
@@ -32,7 +32,7 @@ void initialize_process_messages()
{
static const auto testing_setup = MakeNoLogFileContext<const TestingSetup>(
/*chain_type=*/ChainType::REGTEST,
- /*extra_args=*/{"-txreconciliation"});
+ {.extra_args = {"-txreconciliation"}});
g_setup = testing_setup.get();
for (int i = 0; i < 2 * COINBASE_MATURITY; i++) {
MineBlock(g_setup->m_node, CScript() << OP_TRUE);
diff --git a/src/test/fuzz/random.cpp b/src/test/fuzz/random.cpp
index 96668734fd..6b2d42738b 100644
--- a/src/test/fuzz/random.cpp
+++ b/src/test/fuzz/random.cpp
@@ -26,6 +26,5 @@ FUZZ_TARGET(random)
(void)fast_random_context();
std::vector<int64_t> integrals = ConsumeRandomLengthIntegralVector<int64_t>(fuzzed_data_provider);
- Shuffle(integrals.begin(), integrals.end(), fast_random_context);
std::shuffle(integrals.begin(), integrals.end(), fast_random_context);
}
diff --git a/src/test/fuzz/rpc.cpp b/src/test/fuzz/rpc.cpp
index 4e52c1c091..9122617e46 100644
--- a/src/test/fuzz/rpc.cpp
+++ b/src/test/fuzz/rpc.cpp
@@ -41,7 +41,7 @@ using util::ToString;
namespace {
struct RPCFuzzTestingSetup : public TestingSetup {
- RPCFuzzTestingSetup(const ChainType chain_type, const std::vector<const char*>& extra_args) : TestingSetup{chain_type, extra_args}
+ RPCFuzzTestingSetup(const ChainType chain_type, TestOpts opts) : TestingSetup{chain_type, opts}
{
}
diff --git a/src/test/fuzz/script.cpp b/src/test/fuzz/script.cpp
index fe41a8c6ae..07a49e039f 100644
--- a/src/test/fuzz/script.cpp
+++ b/src/test/fuzz/script.cpp
@@ -94,6 +94,7 @@ FUZZ_TARGET(script, .init = initialize_script)
(void)Solver(script, solutions);
(void)script.HasValidOps();
+ (void)script.IsPayToAnchor();
(void)script.IsPayToScriptHash();
(void)script.IsPayToWitnessScriptHash();
(void)script.IsPushOnly();
diff --git a/src/test/fuzz/script_sigcache.cpp b/src/test/fuzz/script_sigcache.cpp
index 5fdbc9e106..7f73c3706c 100644
--- a/src/test/fuzz/script_sigcache.cpp
+++ b/src/test/fuzz/script_sigcache.cpp
@@ -2,41 +2,41 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <chainparams.h>
-#include <key.h>
+#include <consensus/amount.h>
+#include <primitives/transaction.h>
#include <pubkey.h>
+#include <script/interpreter.h>
#include <script/sigcache.h>
+#include <span.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
#include <test/util/setup_common.h>
+#include <uint256.h>
-#include <cstdint>
+#include <cstddef>
#include <optional>
-#include <string>
#include <vector>
-namespace {
-const BasicTestingSetup* g_setup;
-} // namespace
-
void initialize_script_sigcache()
{
static const auto testing_setup = MakeNoLogFileContext<>();
- g_setup = testing_setup.get();
}
FUZZ_TARGET(script_sigcache, .init = initialize_script_sigcache)
{
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
+ const auto max_sigcache_bytes{fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, DEFAULT_SIGNATURE_CACHE_BYTES)};
+ SignatureCache signature_cache{max_sigcache_bytes};
+
const std::optional<CMutableTransaction> mutable_transaction = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider, TX_WITH_WITNESS);
const CTransaction tx{mutable_transaction ? *mutable_transaction : CMutableTransaction{}};
const unsigned int n_in = fuzzed_data_provider.ConsumeIntegral<unsigned int>();
const CAmount amount = ConsumeMoney(fuzzed_data_provider);
const bool store = fuzzed_data_provider.ConsumeBool();
PrecomputedTransactionData tx_data;
- CachingTransactionSignatureChecker caching_transaction_signature_checker{mutable_transaction ? &tx : nullptr, n_in, amount, store, tx_data};
+ CachingTransactionSignatureChecker caching_transaction_signature_checker{mutable_transaction ? &tx : nullptr, n_in, amount, store, signature_cache, tx_data};
if (fuzzed_data_provider.ConsumeBool()) {
const auto random_bytes = fuzzed_data_provider.ConsumeBytes<unsigned char>(64);
const XOnlyPubKey pub_key(ConsumeUInt256(fuzzed_data_provider));
diff --git a/src/test/fuzz/string.cpp b/src/test/fuzz/string.cpp
index 5b822b03f6..443d7241b5 100644
--- a/src/test/fuzz/string.cpp
+++ b/src/test/fuzz/string.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2020-2022 The Bitcoin Core developers
+// Copyright (c) 2020-present The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -101,7 +101,6 @@ FUZZ_TARGET(string)
(void)TrimString(random_string_1, random_string_2);
(void)UrlDecode(random_string_1);
(void)ContainsNoNUL(random_string_1);
- (void)_(random_string_1.c_str());
try {
throw scriptnum_error{random_string_1};
} catch (const std::runtime_error&) {
diff --git a/src/test/fuzz/txorphan.cpp b/src/test/fuzz/txorphan.cpp
index 7bebb987b1..5129c05a39 100644
--- a/src/test/fuzz/txorphan.cpp
+++ b/src/test/fuzz/txorphan.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2022 The Bitcoin Core developers
+// Copyright (c) 2022-present The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -37,11 +37,11 @@ FUZZ_TARGET(txorphan, .init = initialize_orphanage)
SetMockTime(ConsumeTime(fuzzed_data_provider));
TxOrphanage orphanage;
- std::set<COutPoint> outpoints;
+ std::vector<COutPoint> outpoints; // Duplicates are tolerated
// initial outpoints used to construct transactions later
for (uint8_t i = 0; i < 4; i++) {
- outpoints.emplace(Txid::FromUint256(uint256{i}), 0);
+ outpoints.emplace_back(Txid::FromUint256(uint256{i}), 0);
}
CTransactionRef ptx_potential_parent = nullptr;
@@ -67,7 +67,7 @@ FUZZ_TARGET(txorphan, .init = initialize_orphanage)
auto new_tx = MakeTransactionRef(tx_mut);
// add newly constructed outpoints to the coin pool
for (uint32_t i = 0; i < num_out; i++) {
- outpoints.emplace(new_tx->GetHash(), i);
+ outpoints.emplace_back(new_tx->GetHash(), i);
}
return new_tx;
}();
diff --git a/src/test/fuzz/util.cpp b/src/test/fuzz/util.cpp
index 92ded99917..425b9559a7 100644
--- a/src/test/fuzz/util.cpp
+++ b/src/test/fuzz/util.cpp
@@ -214,6 +214,9 @@ CTxDestination ConsumeTxDestination(FuzzedDataProvider& fuzzed_data_provider) no
tx_destination = WitnessV1Taproot{XOnlyPubKey{ConsumeUInt256(fuzzed_data_provider)}};
},
[&] {
+ tx_destination = PayToAnchor{};
+ },
+ [&] {
std::vector<unsigned char> program{ConsumeRandomLengthByteVector(fuzzed_data_provider, /*max_length=*/40)};
if (program.size() < 2) {
program = {0, 0};
diff --git a/src/test/fuzz/util/descriptor.cpp b/src/test/fuzz/util/descriptor.cpp
index 0fed2bc5e1..9e52e990a2 100644
--- a/src/test/fuzz/util/descriptor.cpp
+++ b/src/test/fuzz/util/descriptor.cpp
@@ -4,6 +4,9 @@
#include <test/fuzz/util/descriptor.h>
+#include <ranges>
+#include <stack>
+
void MockedDescriptorConverter::Init() {
// The data to use as a private key or a seed for an xprv.
std::array<std::byte, 32> key_data{std::byte{1}};
@@ -84,3 +87,59 @@ bool HasDeepDerivPath(const FuzzBufferType& buff, const int max_depth)
}
return false;
}
+
+bool HasTooManySubFrag(const FuzzBufferType& buff, const int max_subs, const size_t max_nested_subs)
+{
+ // We use a stack because there may be many nested sub-frags.
+ std::stack<int> counts;
+ for (const auto& ch: buff) {
+ // The fuzzer may generate an input with a ton of parentheses. Rule out pathological cases.
+ if (counts.size() > max_nested_subs) return true;
+
+ if (ch == '(') {
+ // A new fragment was opened, create a new sub-count for it and start as one since any fragment with
+ // parentheses has at least one sub.
+ counts.push(1);
+ } else if (ch == ',' && !counts.empty()) {
+ // When encountering a comma, account for an additional sub in the last opened fragment. If it exceeds the
+ // limit, bail.
+ if (++counts.top() > max_subs) return true;
+ } else if (ch == ')' && !counts.empty()) {
+ // Fragment closed! Drop its sub count and resume to counting the number of subs for its parent.
+ counts.pop();
+ }
+ }
+ return false;
+}
+
+bool HasTooManyWrappers(const FuzzBufferType& buff, const int max_wrappers)
+{
+ // The number of nested wrappers. Nested wrappers are always characters which follow each other so we don't have to
+ // use a stack as we do above when counting the number of sub-fragments.
+ std::optional<int> count;
+
+ // We want to detect nested wrappers. A wrapper is a character prepended to a fragment, separated by a colon. There
+ // may be more than one wrapper, in which case the colon is not repeated. For instance `jjjjj:pk()`. To count
+ // wrappers we iterate in reverse and use the colon to detect the end of a wrapper expression and count how many
+ // characters there are since the beginning of the expression. We stop counting when we encounter a character
+ // indicating the beginning of a new expression.
+ for (const auto ch: buff | std::views::reverse) {
+ // A colon, start counting.
+ if (ch == ':') {
+ // The colon itself is not a wrapper so we start at 0.
+ count = 0;
+ } else if (count) {
+ // If we are counting wrappers, stop when we crossed the beginning of the wrapper expression. Otherwise keep
+ // counting and bail if we reached the limit.
+ // A wrapper may only ever occur as the first sub of a descriptor/miniscript expression ('('), as the
+ // first Taproot leaf in a pair ('{') or as the nth sub in each case (',').
+ if (ch == ',' || ch == '(' || ch == '{') {
+ count.reset();
+ } else if (++*count > max_wrappers) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
diff --git a/src/test/fuzz/util/descriptor.h b/src/test/fuzz/util/descriptor.h
index cd41dbafa3..ea928c39f0 100644
--- a/src/test/fuzz/util/descriptor.h
+++ b/src/test/fuzz/util/descriptor.h
@@ -55,4 +55,25 @@ constexpr int MAX_DEPTH{2};
*/
bool HasDeepDerivPath(const FuzzBufferType& buff, const int max_depth = MAX_DEPTH);
+//! Default maximum number of sub-fragments.
+constexpr int MAX_SUBS{1'000};
+//! Maximum number of nested sub-fragments we'll allow in a descriptor.
+constexpr size_t MAX_NESTED_SUBS{10'000};
+
+/**
+ * Whether the buffer, if it represents a valid descriptor, contains a fragment with more
+ * sub-fragments than the given maximum.
+ */
+bool HasTooManySubFrag(const FuzzBufferType& buff, const int max_subs = MAX_SUBS,
+ const size_t max_nested_subs = MAX_NESTED_SUBS);
+
+//! Default maximum number of wrappers per fragment.
+constexpr int MAX_WRAPPERS{100};
+
+/**
+ * Whether the buffer, if it represents a valid descriptor, contains a fragment with more
+ * wrappers than the given maximum.
+ */
+bool HasTooManyWrappers(const FuzzBufferType& buff, const int max_wrappers = MAX_WRAPPERS);
+
#endif // BITCOIN_TEST_FUZZ_UTIL_DESCRIPTOR_H
diff --git a/src/test/fuzz/util/net.cpp b/src/test/fuzz/util/net.cpp
index ad69c29d12..ca0fd65cae 100644
--- a/src/test/fuzz/util/net.cpp
+++ b/src/test/fuzz/util/net.cpp
@@ -383,7 +383,7 @@ bool FuzzedSock::Wait(std::chrono::milliseconds timeout, Event requested, Event*
return false;
}
if (occurred != nullptr) {
- // We simulate the requested event as occured when ConsumeBool()
+ // We simulate the requested event as occurred when ConsumeBool()
// returns false. This avoids simulating endless waiting if the
// FuzzedDataProvider runs out of data.
*occurred = m_fuzzed_data_provider.ConsumeBool() ? 0 : requested;
@@ -395,7 +395,7 @@ bool FuzzedSock::WaitMany(std::chrono::milliseconds timeout, EventsPerSock& even
{
for (auto& [sock, events] : events_per_sock) {
(void)sock;
- // We simulate the requested event as occured when ConsumeBool()
+ // We simulate the requested event as occurred when ConsumeBool()
// returns false. This avoids simulating endless waiting if the
// FuzzedDataProvider runs out of data.
events.occurred = m_fuzzed_data_provider.ConsumeBool() ? 0 : events.requested;
diff --git a/src/test/fuzz/utxo_snapshot.cpp b/src/test/fuzz/utxo_snapshot.cpp
index fa608385d9..21c305e222 100644
--- a/src/test/fuzz/utxo_snapshot.cpp
+++ b/src/test/fuzz/utxo_snapshot.cpp
@@ -1,39 +1,79 @@
-// Copyright (c) 2021-2022 The Bitcoin Core developers
+// Copyright (c) 2021-present 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 <chain.h>
#include <chainparams.h>
+#include <coins.h>
+#include <consensus/consensus.h>
#include <consensus/validation.h>
+#include <node/blockstorage.h>
#include <node/utxo_snapshot.h>
+#include <primitives/block.h>
+#include <primitives/transaction.h>
+#include <serialize.h>
+#include <span.h>
+#include <streams.h>
+#include <sync.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
#include <test/util/mining.h>
#include <test/util/setup_common.h>
-#include <util/chaintype.h>
+#include <uint256.h>
+#include <util/check.h>
#include <util/fs.h>
+#include <util/result.h>
#include <validation.h>
-#include <validationinterface.h>
+
+#include <cstdint>
+#include <functional>
+#include <ios>
+#include <memory>
+#include <optional>
+#include <vector>
using node::SnapshotMetadata;
namespace {
const std::vector<std::shared_ptr<CBlock>>* g_chain;
+TestingSetup* g_setup;
+template <bool INVALID>
void initialize_chain()
{
const auto params{CreateChainParams(ArgsManager{}, ChainType::REGTEST)};
static const auto chain{CreateBlockChain(2 * COINBASE_MATURITY, *params)};
g_chain = &chain;
+ static const auto setup{
+ MakeNoLogFileContext<TestingSetup>(ChainType::REGTEST,
+ TestOpts{
+ .setup_net = false,
+ .setup_validation_interface = false,
+ .min_validation_cache = true,
+ }),
+ };
+ if constexpr (INVALID) {
+ auto& chainman{*setup->m_node.chainman};
+ for (const auto& block : chain) {
+ BlockValidationState dummy;
+ bool processed{chainman.ProcessNewBlockHeaders({*block}, true, dummy)};
+ Assert(processed);
+ const auto* index{WITH_LOCK(::cs_main, return chainman.m_blockman.LookupBlockIndex(block->GetHash()))};
+ Assert(index);
+ }
+ }
+ g_setup = setup.get();
}
-FUZZ_TARGET(utxo_snapshot, .init = initialize_chain)
+template <bool INVALID>
+void utxo_snapshot_fuzz(FuzzBufferType buffer)
{
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
- std::unique_ptr<const TestingSetup> setup{MakeNoLogFileContext<const TestingSetup>()};
- const auto& node = setup->m_node;
- auto& chainman{*node.chainman};
+ auto& setup{*g_setup};
+ bool dirty_chainman{false}; // Re-use the global chainman, but reset it when it is dirty
+ auto& chainman{*setup.m_node.chainman};
const auto snapshot_path = gArgs.GetDataDirNet() / "fuzzed_snapshot.dat";
@@ -41,13 +81,48 @@ FUZZ_TARGET(utxo_snapshot, .init = initialize_chain)
{
AutoFile outfile{fsbridge::fopen(snapshot_path, "wb")};
- const auto file_data{ConsumeRandomLengthByteVector(fuzzed_data_provider)};
- outfile << Span{file_data};
+ // Metadata
+ if (fuzzed_data_provider.ConsumeBool()) {
+ std::vector<uint8_t> metadata{ConsumeRandomLengthByteVector(fuzzed_data_provider)};
+ outfile << Span{metadata};
+ } else {
+ auto msg_start = chainman.GetParams().MessageStart();
+ int base_blockheight{fuzzed_data_provider.ConsumeIntegralInRange<int>(1, 2 * COINBASE_MATURITY)};
+ uint256 base_blockhash{g_chain->at(base_blockheight - 1)->GetHash()};
+ uint64_t m_coins_count{fuzzed_data_provider.ConsumeIntegralInRange<uint64_t>(1, 3 * COINBASE_MATURITY)};
+ SnapshotMetadata metadata{msg_start, base_blockhash, m_coins_count};
+ outfile << metadata;
+ }
+ // Coins
+ if (fuzzed_data_provider.ConsumeBool()) {
+ std::vector<uint8_t> file_data{ConsumeRandomLengthByteVector(fuzzed_data_provider)};
+ outfile << Span{file_data};
+ } else {
+ int height{0};
+ for (const auto& block : *g_chain) {
+ auto coinbase{block->vtx.at(0)};
+ outfile << coinbase->GetHash();
+ WriteCompactSize(outfile, 1); // number of coins for the hash
+ WriteCompactSize(outfile, 0); // index of coin
+ outfile << Coin(coinbase->vout[0], height, /*fCoinBaseIn=*/1);
+ height++;
+ }
+ }
+ if constexpr (INVALID) {
+ // Append an invalid coin to ensure invalidity. This error will be
+ // detected late in PopulateAndValidateSnapshot, and allows the
+ // INVALID fuzz target to reach more potential code coverage.
+ const auto& coinbase{g_chain->back()->vtx.back()};
+ outfile << coinbase->GetHash();
+ WriteCompactSize(outfile, 1); // number of coins for the hash
+ WriteCompactSize(outfile, 999); // index of coin
+ outfile << Coin{coinbase->vout[0], /*nHeightIn=*/999, /*fCoinBaseIn=*/0};
+ }
}
const auto ActivateFuzzedSnapshot{[&] {
AutoFile infile{fsbridge::fopen(snapshot_path, "rb")};
- auto msg_start = Params().MessageStart();
+ auto msg_start = chainman.GetParams().MessageStart();
SnapshotMetadata metadata{msg_start};
try {
infile >> metadata;
@@ -58,12 +133,16 @@ FUZZ_TARGET(utxo_snapshot, .init = initialize_chain)
}};
if (fuzzed_data_provider.ConsumeBool()) {
- for (const auto& block : *g_chain) {
- BlockValidationState dummy;
- bool processed{chainman.ProcessNewBlockHeaders({*block}, true, dummy)};
- Assert(processed);
- const auto* index{WITH_LOCK(::cs_main, return chainman.m_blockman.LookupBlockIndex(block->GetHash()))};
- Assert(index);
+ // Consume the bool, but skip the code for the INVALID fuzz target
+ if constexpr (!INVALID) {
+ for (const auto& block : *g_chain) {
+ BlockValidationState dummy;
+ bool processed{chainman.ProcessNewBlockHeaders({*block}, true, dummy)};
+ Assert(processed);
+ const auto* index{WITH_LOCK(::cs_main, return chainman.m_blockman.LookupBlockIndex(block->GetHash()))};
+ Assert(index);
+ }
+ dirty_chainman = true;
}
}
@@ -73,21 +152,49 @@ FUZZ_TARGET(utxo_snapshot, .init = initialize_chain)
Assert(*chainman.ActiveChainstate().m_from_snapshot_blockhash ==
*chainman.SnapshotBlockhash());
const auto& coinscache{chainman.ActiveChainstate().CoinsTip()};
- int64_t chain_tx{};
for (const auto& block : *g_chain) {
Assert(coinscache.HaveCoin(COutPoint{block->vtx.at(0)->GetHash(), 0}));
const auto* index{chainman.m_blockman.LookupBlockIndex(block->GetHash())};
- const auto num_tx{Assert(index)->nTx};
- Assert(num_tx == 1);
- chain_tx += num_tx;
+ Assert(index);
+ Assert(index->nTx == 0);
+ if (index->nHeight == chainman.GetSnapshotBaseHeight()) {
+ auto params{chainman.GetParams().AssumeutxoForHeight(index->nHeight)};
+ Assert(params.has_value());
+ Assert(params.value().m_chain_tx_count == index->m_chain_tx_count);
+ } else {
+ Assert(index->m_chain_tx_count == 0);
+ }
}
Assert(g_chain->size() == coinscache.GetCacheSize());
- Assert(chain_tx == chainman.ActiveTip()->nChainTx);
+ dirty_chainman = true;
} else {
Assert(!chainman.SnapshotBlockhash());
Assert(!chainman.ActiveChainstate().m_from_snapshot_blockhash);
}
// Snapshot should refuse to load a second time regardless of validity
Assert(!ActivateFuzzedSnapshot());
+ if constexpr (INVALID) {
+ // Activating the snapshot, or any other action that makes the chainman
+ // "dirty" can and must not happen for the INVALID fuzz target
+ Assert(!dirty_chainman);
+ }
+ if (dirty_chainman) {
+ setup.m_node.chainman.reset();
+ setup.m_make_chainman();
+ setup.LoadVerifyActivateChainstate();
+ }
}
+
+// There are two fuzz targets:
+//
+// The target 'utxo_snapshot', which allows valid snapshots, but is slow,
+// because it has to reset the chainstate manager on almost all fuzz inputs.
+// Otherwise, a dirty header tree or dirty chainstate could leak from one fuzz
+// input execution into the next, which makes execution non-deterministic.
+//
+// The target 'utxo_snapshot_invalid', which is fast and does not require any
+// expensive state to be reset.
+FUZZ_TARGET(utxo_snapshot /*valid*/, .init = initialize_chain<false>) { utxo_snapshot_fuzz<false>(buffer); }
+FUZZ_TARGET(utxo_snapshot_invalid, .init = initialize_chain<true>) { utxo_snapshot_fuzz<true>(buffer); }
+
} // namespace
diff --git a/src/test/fuzz/utxo_total_supply.cpp b/src/test/fuzz/utxo_total_supply.cpp
index 48ed266abe..b0f1a1251a 100644
--- a/src/test/fuzz/utxo_total_supply.cpp
+++ b/src/test/fuzz/utxo_total_supply.cpp
@@ -23,7 +23,7 @@ FUZZ_TARGET(utxo_total_supply)
ChainTestingSetup test_setup{
ChainType::REGTEST,
{
- "-testactivationheight=bip34@2",
+ .extra_args = {"-testactivationheight=bip34@2"},
},
};
// Create chainstate
diff --git a/src/test/fuzz/vecdeque.cpp b/src/test/fuzz/vecdeque.cpp
index 1d9a98931f..3bb858ee8a 100644
--- a/src/test/fuzz/vecdeque.cpp
+++ b/src/test/fuzz/vecdeque.cpp
@@ -2,9 +2,9 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <random.h>
#include <span.h>
#include <test/fuzz/util.h>
-#include <test/util/xoroshiro128plusplus.h>
#include <util/vecdeque.h>
#include <deque>
@@ -28,7 +28,7 @@ void TestType(Span<const uint8_t> buffer, uint64_t rng_tweak)
{
FuzzedDataProvider provider(buffer.data(), buffer.size());
// Local RNG, only used for the seeds to initialize T objects with.
- XoRoShiRo128PlusPlus rng(provider.ConsumeIntegral<uint64_t>() ^ rng_tweak);
+ InsecureRandomContext rng(provider.ConsumeIntegral<uint64_t>() ^ rng_tweak);
// Real circular buffers.
std::vector<VecDeque<T>> real;
@@ -175,7 +175,7 @@ void TestType(Span<const uint8_t> buffer, uint64_t rng_tweak)
}
if (existing_buffer_non_full && command-- == 0) {
/* push_back() (copying) */
- tmp = T(rng());
+ tmp = T(rng.rand64());
size_t old_size = real[idx].size();
size_t old_cap = real[idx].capacity();
real[idx].push_back(*tmp);
@@ -191,7 +191,7 @@ void TestType(Span<const uint8_t> buffer, uint64_t rng_tweak)
}
if (existing_buffer_non_full && command-- == 0) {
/* push_back() (moving) */
- tmp = T(rng());
+ tmp = T(rng.rand64());
size_t old_size = real[idx].size();
size_t old_cap = real[idx].capacity();
sim[idx].push_back(*tmp);
@@ -207,7 +207,7 @@ void TestType(Span<const uint8_t> buffer, uint64_t rng_tweak)
}
if (existing_buffer_non_full && command-- == 0) {
/* emplace_back() */
- uint64_t seed{rng()};
+ uint64_t seed{rng.rand64()};
size_t old_size = real[idx].size();
size_t old_cap = real[idx].capacity();
sim[idx].emplace_back(seed);
@@ -223,7 +223,7 @@ void TestType(Span<const uint8_t> buffer, uint64_t rng_tweak)
}
if (existing_buffer_non_full && command-- == 0) {
/* push_front() (copying) */
- tmp = T(rng());
+ tmp = T(rng.rand64());
size_t old_size = real[idx].size();
size_t old_cap = real[idx].capacity();
real[idx].push_front(*tmp);
@@ -239,7 +239,7 @@ void TestType(Span<const uint8_t> buffer, uint64_t rng_tweak)
}
if (existing_buffer_non_full && command-- == 0) {
/* push_front() (moving) */
- tmp = T(rng());
+ tmp = T(rng.rand64());
size_t old_size = real[idx].size();
size_t old_cap = real[idx].capacity();
sim[idx].push_front(*tmp);
@@ -255,7 +255,7 @@ void TestType(Span<const uint8_t> buffer, uint64_t rng_tweak)
}
if (existing_buffer_non_full && command-- == 0) {
/* emplace_front() */
- uint64_t seed{rng()};
+ uint64_t seed{rng.rand64()};
size_t old_size = real[idx].size();
size_t old_cap = real[idx].capacity();
sim[idx].emplace_front(seed);
@@ -271,7 +271,7 @@ void TestType(Span<const uint8_t> buffer, uint64_t rng_tweak)
}
if (existing_buffer_non_empty && command-- == 0) {
/* front() [modifying] */
- tmp = T(rng());
+ tmp = T(rng.rand64());
size_t old_size = real[idx].size();
assert(sim[idx].front() == real[idx].front());
sim[idx].front() = *tmp;
@@ -281,7 +281,7 @@ void TestType(Span<const uint8_t> buffer, uint64_t rng_tweak)
}
if (existing_buffer_non_empty && command-- == 0) {
/* back() [modifying] */
- tmp = T(rng());
+ tmp = T(rng.rand64());
size_t old_size = real[idx].size();
assert(sim[idx].back() == real[idx].back());
sim[idx].back() = *tmp;
@@ -291,7 +291,7 @@ void TestType(Span<const uint8_t> buffer, uint64_t rng_tweak)
}
if (existing_buffer_non_empty && command-- == 0) {
/* operator[] [modifying] */
- tmp = T(rng());
+ tmp = T(rng.rand64());
size_t pos = provider.ConsumeIntegralInRange<size_t>(0, sim[idx].size() - 1);
size_t old_size = real[idx].size();
assert(sim[idx][pos] == real[idx][pos]);
diff --git a/src/test/hash_tests.cpp b/src/test/hash_tests.cpp
index 51f1d4c840..150e386c2d 100644
--- a/src/test/hash_tests.cpp
+++ b/src/test/hash_tests.cpp
@@ -104,7 +104,7 @@ BOOST_AUTO_TEST_CASE(siphash)
hasher.Write(0x2F2E2D2C2B2A2928ULL);
BOOST_CHECK_EQUAL(hasher.Finalize(), 0xe612a3cb9ecba951ull);
- BOOST_CHECK_EQUAL(SipHashUint256(0x0706050403020100ULL, 0x0F0E0D0C0B0A0908ULL, uint256S("1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100")), 0x7127512f72f27cceull);
+ BOOST_CHECK_EQUAL(SipHashUint256(0x0706050403020100ULL, 0x0F0E0D0C0B0A0908ULL, uint256{"1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100"}), 0x7127512f72f27cceull);
// Check test vectors from spec, one byte at a time
CSipHasher hasher2(0x0706050403020100ULL, 0x0F0E0D0C0B0A0908ULL);
diff --git a/src/test/i2p_tests.cpp b/src/test/i2p_tests.cpp
index 0512c6134f..bb9ca88019 100644
--- a/src/test/i2p_tests.cpp
+++ b/src/test/i2p_tests.cpp
@@ -23,8 +23,8 @@ class EnvTestingSetup : public BasicTestingSetup
{
public:
explicit EnvTestingSetup(const ChainType chainType = ChainType::MAIN,
- const std::vector<const char*>& extra_args = {})
- : BasicTestingSetup{chainType, extra_args},
+ TestOpts opts = {})
+ : BasicTestingSetup{chainType, opts},
m_prev_log_level{LogInstance().LogLevel()},
m_create_sock_orig{CreateSock}
{
diff --git a/src/test/key_tests.cpp b/src/test/key_tests.cpp
index b897a0a153..112d6db193 100644
--- a/src/test/key_tests.cpp
+++ b/src/test/key_tests.cpp
@@ -8,6 +8,7 @@
#include <key_io.h>
#include <span.h>
#include <streams.h>
+#include <secp256k1_extrakeys.h>
#include <test/util/random.h>
#include <test/util/setup_common.h>
#include <uint256.h>
@@ -299,6 +300,13 @@ BOOST_AUTO_TEST_CASE(bip340_test_vectors)
// Verify those signatures for good measure.
BOOST_CHECK(pubkey.VerifySchnorr(msg256, sig64));
+ // Repeat the same check, but use the KeyPair directly without any merkle tweak
+ KeyPair keypair = key.ComputeKeyPair(/*merkle_root=*/nullptr);
+ bool kp_ok = keypair.SignSchnorr(msg256, sig64, aux256);
+ BOOST_CHECK(kp_ok);
+ BOOST_CHECK(pubkey.VerifySchnorr(msg256, sig64));
+ BOOST_CHECK(std::vector<unsigned char>(sig64, sig64 + 64) == sig);
+
// Do 10 iterations where we sign with a random Merkle root to tweak,
// and compare against the resulting tweaked keys, with random aux.
// In iteration i=0 we tweak with empty Merkle tree.
@@ -312,6 +320,12 @@ BOOST_AUTO_TEST_CASE(bip340_test_vectors)
bool ok = key.SignSchnorr(msg256, sig64, &merkle_root, aux256);
BOOST_CHECK(ok);
BOOST_CHECK(tweaked_key.VerifySchnorr(msg256, sig64));
+
+ // Repeat the same check, but use the KeyPair class directly
+ KeyPair keypair = key.ComputeKeyPair(&merkle_root);
+ bool kp_ok = keypair.SignSchnorr(msg256, sig64, aux256);
+ BOOST_CHECK(kp_ok);
+ BOOST_CHECK(tweaked_key.VerifySchnorr(msg256, sig64));
}
}
}
@@ -345,4 +359,31 @@ BOOST_AUTO_TEST_CASE(bip341_test_h)
BOOST_CHECK(XOnlyPubKey::NUMS_H == H);
}
+BOOST_AUTO_TEST_CASE(key_schnorr_tweak_smoke_test)
+{
+ // Sanity check to ensure we get the same tweak using CPubKey vs secp256k1 functions
+ secp256k1_context* secp256k1_context_sign = secp256k1_context_create(SECP256K1_CONTEXT_SIGN);
+
+ CKey key;
+ key.MakeNewKey(true);
+ uint256 merkle_root = InsecureRand256();
+
+ // secp256k1 functions
+ secp256k1_keypair keypair;
+ BOOST_CHECK(secp256k1_keypair_create(secp256k1_context_sign, &keypair, UCharCast(key.begin())));
+ secp256k1_xonly_pubkey xonly_pubkey;
+ BOOST_CHECK(secp256k1_keypair_xonly_pub(secp256k1_context_sign, &xonly_pubkey, nullptr, &keypair));
+ unsigned char xonly_bytes[32];
+ BOOST_CHECK(secp256k1_xonly_pubkey_serialize(secp256k1_context_sign, xonly_bytes, &xonly_pubkey));
+ uint256 tweak_old = XOnlyPubKey(xonly_bytes).ComputeTapTweakHash(&merkle_root);
+
+ // CPubKey
+ CPubKey pubkey = key.GetPubKey();
+ uint256 tweak_new = XOnlyPubKey(pubkey).ComputeTapTweakHash(&merkle_root);
+
+ BOOST_CHECK_EQUAL(tweak_old, tweak_new);
+
+ secp256k1_context_destroy(secp256k1_context_sign);
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/merkleblock_tests.cpp b/src/test/merkleblock_tests.cpp
index 9c6008bdca..73dbe33714 100644
--- a/src/test/merkleblock_tests.cpp
+++ b/src/test/merkleblock_tests.cpp
@@ -24,10 +24,10 @@ BOOST_AUTO_TEST_CASE(merkleblock_construct_from_txids_found)
std::set<Txid> txids;
// Last txn in block.
- Txid txhash1{TxidFromString("0x74d681e0e03bafa802c8aa084379aa98d9fcd632ddc2ed9782b586ec87451f20")};
+ Txid txhash1{Txid::FromHex("74d681e0e03bafa802c8aa084379aa98d9fcd632ddc2ed9782b586ec87451f20").value()};
// Second txn in block.
- Txid txhash2{TxidFromString("0xf9fc751cb7dc372406a9f8d738d5e6f8f63bab71986a39cf36ee70ee17036d07")};
+ Txid txhash2{Txid::FromHex("f9fc751cb7dc372406a9f8d738d5e6f8f63bab71986a39cf36ee70ee17036d07").value()};
txids.insert(txhash1);
txids.insert(txhash2);
@@ -63,7 +63,7 @@ BOOST_AUTO_TEST_CASE(merkleblock_construct_from_txids_not_found)
CBlock block = getBlock13b8a();
std::set<Txid> txids2;
- txids2.insert(TxidFromString("0xc0ffee00003bafa802c8aa084379aa98d9fcd632ddc2ed9782b586ec87451f20"));
+ txids2.insert(Txid::FromHex("c0ffee00003bafa802c8aa084379aa98d9fcd632ddc2ed9782b586ec87451f20").value());
CMerkleBlock merkleBlock(block, txids2);
BOOST_CHECK_EQUAL(merkleBlock.header.GetHash().GetHex(), block.GetHash().GetHex());
diff --git a/src/test/miniscript_tests.cpp b/src/test/miniscript_tests.cpp
index 7e39e9e4de..815c278b8c 100644
--- a/src/test/miniscript_tests.cpp
+++ b/src/test/miniscript_tests.cpp
@@ -48,9 +48,9 @@ struct TestData {
TestData()
{
// All our signatures sign (and are required to sign) this constant message.
- auto const MESSAGE_HASH = uint256S("f5cd94e18b6fe77dd7aca9e35c2b0c9cbd86356c80a71065");
+ constexpr uint256 MESSAGE_HASH{"0000000000000000f5cd94e18b6fe77dd7aca9e35c2b0c9cbd86356c80a71065"};
// We don't pass additional randomness when creating a schnorr signature.
- auto const EMPTY_AUX{uint256S("")};
+ const auto EMPTY_AUX{uint256::ZERO};
// We generate 255 public keys and 255 hashes of each type.
for (int i = 1; i <= 255; ++i) {
@@ -346,7 +346,7 @@ void TestSatisfy(const KeyConverter& converter, const std::string& testcase, con
auto challenges = FindChallenges(node); // Find all challenges in the generated miniscript.
std::vector<Challenge> challist(challenges.begin(), challenges.end());
for (int iter = 0; iter < 3; ++iter) {
- Shuffle(challist.begin(), challist.end(), g_insecure_rand_ctx);
+ std::shuffle(challist.begin(), challist.end(), g_insecure_rand_ctx);
Satisfier satisfier(converter.MsContext());
TestSignatureChecker checker(satisfier);
bool prev_mal_success = false, prev_nonmal_success = false;
@@ -699,6 +699,12 @@ BOOST_AUTO_TEST_CASE(fixed_tests)
const auto insane_sub = ms_ins->FindInsaneSub();
BOOST_CHECK(insane_sub && *insane_sub->ToString(wsh_converter) == "and_b(after(1),a:after(1000000000))");
+ // Numbers can't be prefixed by a sign.
+ BOOST_CHECK(!miniscript::FromString("after(-1)", wsh_converter));
+ BOOST_CHECK(!miniscript::FromString("after(+1)", wsh_converter));
+ BOOST_CHECK(!miniscript::FromString("thresh(-1,pk(03cdabb7f2dce7bfbd8a0b9570c6fd1e712e5d64045e9d6b517b3d5072251dc204))", wsh_converter));
+ BOOST_CHECK(!miniscript::FromString("multi(+1,03cdabb7f2dce7bfbd8a0b9570c6fd1e712e5d64045e9d6b517b3d5072251dc204)", wsh_converter));
+
// Timelock tests
Test("after(100)", "?", "?", TESTMODE_VALID | TESTMODE_NONMAL); // only heightlock
Test("after(1000000000)", "?", "?", TESTMODE_VALID | TESTMODE_NONMAL); // only timelock
diff --git a/src/test/net_peer_connection_tests.cpp b/src/test/net_peer_connection_tests.cpp
index 5f38ce112c..2dde6daee5 100644
--- a/src/test/net_peer_connection_tests.cpp
+++ b/src/test/net_peer_connection_tests.cpp
@@ -31,7 +31,7 @@
struct LogIPsTestingSetup : public TestingSetup {
LogIPsTestingSetup()
- : TestingSetup{ChainType::MAIN, /*extra_args=*/{"-logips"}} {}
+ : TestingSetup{ChainType::MAIN, {.extra_args = {"-logips"}}} {}
};
BOOST_FIXTURE_TEST_SUITE(net_peer_connection_tests, LogIPsTestingSetup)
diff --git a/src/test/net_peer_eviction_tests.cpp b/src/test/net_peer_eviction_tests.cpp
index 51d6c4384a..d9e1c2332e 100644
--- a/src/test/net_peer_eviction_tests.cpp
+++ b/src/test/net_peer_eviction_tests.cpp
@@ -31,7 +31,7 @@ bool IsProtected(int num_peers,
for (NodeEvictionCandidate& candidate : candidates) {
candidate_setup_fn(candidate);
}
- Shuffle(candidates.begin(), candidates.end(), random_context);
+ std::shuffle(candidates.begin(), candidates.end(), random_context);
const size_t size{candidates.size()};
const size_t expected{size - size / 2}; // Expect half the candidates will be protected.
@@ -572,7 +572,7 @@ BOOST_AUTO_TEST_CASE(peer_protection_test)
// Returns true if any of the node ids in node_ids are selected for eviction.
bool IsEvicted(std::vector<NodeEvictionCandidate> candidates, const std::unordered_set<NodeId>& node_ids, FastRandomContext& random_context)
{
- Shuffle(candidates.begin(), candidates.end(), random_context);
+ std::shuffle(candidates.begin(), candidates.end(), random_context);
const std::optional<NodeId> evicted_node_id = SelectNodeToEvict(std::move(candidates));
if (!evicted_node_id) {
return false;
diff --git a/src/test/orphanage_tests.cpp b/src/test/orphanage_tests.cpp
index 450bf6a4fc..d2dab94526 100644
--- a/src/test/orphanage_tests.cpp
+++ b/src/test/orphanage_tests.cpp
@@ -21,15 +21,13 @@ BOOST_FIXTURE_TEST_SUITE(orphanage_tests, TestingSetup)
class TxOrphanageTest : public TxOrphanage
{
public:
- inline size_t CountOrphans() const EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
+ inline size_t CountOrphans() const
{
- LOCK(m_mutex);
return m_orphans.size();
}
- CTransactionRef RandomOrphan() EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
+ CTransactionRef RandomOrphan()
{
- LOCK(m_mutex);
std::map<Wtxid, OrphanTx>::iterator it;
it = m_orphans.lower_bound(Wtxid::FromUint256(InsecureRand256()));
if (it == m_orphans.end())
@@ -106,7 +104,7 @@ BOOST_AUTO_TEST_CASE(DoS_mapOrphans)
// ecdsa_signature_parse_der_lax are executed during this test.
// Specifically branches that run only when an ECDSA
// signature's R and S values have leading zeros.
- g_insecure_rand_ctx = FastRandomContext{uint256{33}};
+ g_insecure_rand_ctx.Reseed(uint256{33});
TxOrphanageTest orphanage;
CKey key;
@@ -114,6 +112,10 @@ BOOST_AUTO_TEST_CASE(DoS_mapOrphans)
FillableSigningProvider keystore;
BOOST_CHECK(keystore.AddKey(key));
+ // Freeze time for length of test
+ auto now{GetTime<std::chrono::seconds>()};
+ SetMockTime(now);
+
// 50 orphan transactions:
for (int i = 0; i < 50; i++)
{
@@ -172,22 +174,52 @@ BOOST_AUTO_TEST_CASE(DoS_mapOrphans)
BOOST_CHECK(!orphanage.AddTx(MakeTransactionRef(tx), i));
}
- // Test EraseOrphansFor:
+ size_t expected_num_orphans = orphanage.CountOrphans();
+
+ // Non-existent peer; nothing should be deleted
+ orphanage.EraseForPeer(/*peer=*/-1);
+ BOOST_CHECK_EQUAL(orphanage.CountOrphans(), expected_num_orphans);
+
+ // Each of first three peers stored
+ // two transactions each.
for (NodeId i = 0; i < 3; i++)
{
- size_t sizeBefore = orphanage.CountOrphans();
orphanage.EraseForPeer(i);
- BOOST_CHECK(orphanage.CountOrphans() < sizeBefore);
+ expected_num_orphans -= 2;
+ BOOST_CHECK(orphanage.CountOrphans() == expected_num_orphans);
}
- // Test LimitOrphanTxSize() function:
+ // Test LimitOrphanTxSize() function, nothing should timeout:
FastRandomContext rng{/*fDeterministic=*/true};
+ orphanage.LimitOrphans(/*max_orphans=*/expected_num_orphans, rng);
+ BOOST_CHECK_EQUAL(orphanage.CountOrphans(), expected_num_orphans);
+ expected_num_orphans -= 1;
+ orphanage.LimitOrphans(/*max_orphans=*/expected_num_orphans, rng);
+ BOOST_CHECK_EQUAL(orphanage.CountOrphans(), expected_num_orphans);
+ assert(expected_num_orphans > 40);
orphanage.LimitOrphans(40, rng);
- BOOST_CHECK(orphanage.CountOrphans() <= 40);
+ BOOST_CHECK_EQUAL(orphanage.CountOrphans(), 40);
orphanage.LimitOrphans(10, rng);
- BOOST_CHECK(orphanage.CountOrphans() <= 10);
+ BOOST_CHECK_EQUAL(orphanage.CountOrphans(), 10);
orphanage.LimitOrphans(0, rng);
- BOOST_CHECK(orphanage.CountOrphans() == 0);
+ BOOST_CHECK_EQUAL(orphanage.CountOrphans(), 0);
+
+ // Add one more orphan, check timeout logic
+ auto timeout_tx = MakeTransactionSpending(/*outpoints=*/{}, rng);
+ orphanage.AddTx(timeout_tx, 0);
+ orphanage.LimitOrphans(1, rng);
+ BOOST_CHECK_EQUAL(orphanage.CountOrphans(), 1);
+
+ // One second shy of expiration
+ SetMockTime(now + ORPHAN_TX_EXPIRE_TIME - 1s);
+ orphanage.LimitOrphans(1, rng);
+ BOOST_CHECK_EQUAL(orphanage.CountOrphans(), 1);
+
+ // Jump one more second, orphan should be timed out on limiting
+ SetMockTime(now + ORPHAN_TX_EXPIRE_TIME);
+ BOOST_CHECK_EQUAL(orphanage.CountOrphans(), 1);
+ orphanage.LimitOrphans(1, rng);
+ BOOST_CHECK_EQUAL(orphanage.CountOrphans(), 0);
}
BOOST_AUTO_TEST_CASE(same_txid_diff_witness)
diff --git a/src/test/pmt_tests.cpp b/src/test/pmt_tests.cpp
index edecf70c6f..5b7ccc8e7a 100644
--- a/src/test/pmt_tests.cpp
+++ b/src/test/pmt_tests.cpp
@@ -29,10 +29,10 @@ BOOST_FIXTURE_TEST_SUITE(pmt_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(pmt_test1)
{
- static const unsigned int nTxCounts[] = {1, 4, 7, 17, 56, 100, 127, 256, 312, 513, 1000, 4095};
+ static const unsigned int tx_counts[] = {1, 4, 7, 17, 56, 100, 127, 256, 312, 513, 1000, 4095};
for (int i = 0; i < 12; i++) {
- unsigned int nTx = nTxCounts[i];
+ unsigned int nTx = tx_counts[i];
// build a block with some dummy transactions
CBlock block;
diff --git a/src/test/policyestimator_tests.cpp b/src/test/policyestimator_tests.cpp
index 6cadc3290a..83977c1c89 100644
--- a/src/test/policyestimator_tests.cpp
+++ b/src/test/policyestimator_tests.cpp
@@ -1,8 +1,9 @@
-// Copyright (c) 2011-2022 The Bitcoin Core developers
+// Copyright (c) 2011-present 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 <policy/fees.h>
+#include <policy/fees_args.h>
#include <policy/policy.h>
#include <test/util/txmempool.h>
#include <txmempool.h>
@@ -18,7 +19,7 @@ BOOST_FIXTURE_TEST_SUITE(policyestimator_tests, ChainTestingSetup)
BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
{
- CBlockPolicyEstimator& feeEst = *Assert(m_node.fee_estimator);
+ CBlockPolicyEstimator feeEst{FeeestPath(*m_node.args), DEFAULT_ACCEPT_STALE_FEE_ESTIMATES};
CTxMemPool& mpool = *Assert(m_node.mempool);
m_node.validation_signals->RegisterValidationInterface(&feeEst);
TestMemPoolEntryHelper entry;
diff --git a/src/test/pow_tests.cpp b/src/test/pow_tests.cpp
index 3a44d1da49..4af66af283 100644
--- a/src/test/pow_tests.cpp
+++ b/src/test/pow_tests.cpp
@@ -86,7 +86,7 @@ BOOST_AUTO_TEST_CASE(CheckProofOfWork_test_negative_target)
uint256 hash;
unsigned int nBits;
nBits = UintToArith256(consensus.powLimit).GetCompact(true);
- hash.SetHex("0x1");
+ hash = uint256{1};
BOOST_CHECK(!CheckProofOfWork(hash, nBits, consensus));
}
@@ -95,7 +95,7 @@ BOOST_AUTO_TEST_CASE(CheckProofOfWork_test_overflow_target)
const auto consensus = CreateChainParams(*m_node.args, ChainType::MAIN)->GetConsensus();
uint256 hash;
unsigned int nBits{~0x00800000U};
- hash.SetHex("0x1");
+ hash = uint256{1};
BOOST_CHECK(!CheckProofOfWork(hash, nBits, consensus));
}
@@ -107,7 +107,7 @@ BOOST_AUTO_TEST_CASE(CheckProofOfWork_test_too_easy_target)
arith_uint256 nBits_arith = UintToArith256(consensus.powLimit);
nBits_arith *= 2;
nBits = nBits_arith.GetCompact();
- hash.SetHex("0x1");
+ hash = uint256{1};
BOOST_CHECK(!CheckProofOfWork(hash, nBits, consensus));
}
@@ -177,7 +177,7 @@ void sanity_check_chainparams(const ArgsManager& args, ChainType chain_type)
// check max target * 4*nPowTargetTimespan doesn't overflow -- see pow.cpp:CalculateNextWorkRequired()
if (!consensus.fPowNoRetargeting) {
- arith_uint256 targ_max{UintToArith256(uint256S("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"))};
+ arith_uint256 targ_max{UintToArith256(uint256{"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"})};
targ_max /= consensus.nPowTargetTimespan*4;
BOOST_CHECK(UintToArith256(consensus.powLimit) < targ_max);
}
@@ -198,6 +198,11 @@ BOOST_AUTO_TEST_CASE(ChainParams_TESTNET_sanity)
sanity_check_chainparams(*m_node.args, ChainType::TESTNET);
}
+BOOST_AUTO_TEST_CASE(ChainParams_TESTNET4_sanity)
+{
+ sanity_check_chainparams(*m_node.args, ChainType::TESTNET4);
+}
+
BOOST_AUTO_TEST_CASE(ChainParams_SIGNET_sanity)
{
sanity_check_chainparams(*m_node.args, ChainType::SIGNET);
diff --git a/src/test/prevector_tests.cpp b/src/test/prevector_tests.cpp
index 1559011fcd..f5f0cbee58 100644
--- a/src/test/prevector_tests.cpp
+++ b/src/test/prevector_tests.cpp
@@ -3,17 +3,16 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <prevector.h>
-#include <vector>
-
-#include <reverse_iterator.h>
#include <serialize.h>
#include <streams.h>
-
#include <test/util/random.h>
#include <test/util/setup_common.h>
#include <boost/test/unit_test.hpp>
+#include <ranges>
+#include <vector>
+
BOOST_FIXTURE_TEST_SUITE(prevector_tests, TestingSetup)
template<unsigned int N, typename T>
@@ -58,14 +57,14 @@ class prevector_tester {
for (const T& v : pre_vector) {
local_check(v == real_vector[pos++]);
}
- for (const T& v : reverse_iterate(pre_vector)) {
- local_check(v == real_vector[--pos]);
+ for (const T& v : pre_vector | std::views::reverse) {
+ local_check(v == real_vector[--pos]);
}
for (const T& v : const_pre_vector) {
local_check(v == real_vector[pos++]);
}
- for (const T& v : reverse_iterate(const_pre_vector)) {
- local_check(v == real_vector[--pos]);
+ for (const T& v : const_pre_vector | std::views::reverse) {
+ local_check(v == real_vector[--pos]);
}
DataStream ss1{};
DataStream ss2{};
@@ -210,9 +209,9 @@ public:
}
prevector_tester() {
- SeedInsecureRand();
+ SeedRandomForTest();
rand_seed = InsecureRand256();
- rand_cache = FastRandomContext(rand_seed);
+ rand_cache.Reseed(rand_seed);
}
};
diff --git a/src/test/random_tests.cpp b/src/test/random_tests.cpp
index 43d887b5c9..3d8b543e64 100644
--- a/src/test/random_tests.cpp
+++ b/src/test/random_tests.cpp
@@ -20,28 +20,39 @@ BOOST_AUTO_TEST_CASE(osrandom_tests)
BOOST_CHECK(Random_SanityCheck());
}
-BOOST_AUTO_TEST_CASE(fastrandom_tests)
+BOOST_AUTO_TEST_CASE(fastrandom_tests_deterministic)
{
// Check that deterministic FastRandomContexts are deterministic
- g_mock_deterministic_tests = true;
- FastRandomContext ctx1(true);
- FastRandomContext ctx2(true);
-
- for (int i = 10; i > 0; --i) {
- BOOST_CHECK_EQUAL(GetRand<uint64_t>(), uint64_t{10393729187455219830U});
- BOOST_CHECK_EQUAL(GetRand<int>(), int{769702006});
- BOOST_CHECK_EQUAL(GetRandMicros(std::chrono::hours{1}).count(), 2917185654);
- BOOST_CHECK_EQUAL(GetRandMillis(std::chrono::hours{1}).count(), 2144374);
+ SeedRandomForTest(SeedRand::ZEROS);
+ FastRandomContext ctx1{true};
+ FastRandomContext ctx2{true};
+
+ {
+ BOOST_CHECK_EQUAL(FastRandomContext().rand<uint64_t>(), uint64_t{9330418229102544152u});
+ BOOST_CHECK_EQUAL(FastRandomContext().rand<int>(), int{618925161});
+ BOOST_CHECK_EQUAL(FastRandomContext().randrange<std::chrono::microseconds>(1h).count(), 1271170921);
+ BOOST_CHECK_EQUAL(FastRandomContext().randrange<std::chrono::milliseconds>(1h).count(), 2803534);
+
+ BOOST_CHECK_EQUAL(FastRandomContext().rand<uint64_t>(), uint64_t{10170981140880778086u});
+ BOOST_CHECK_EQUAL(FastRandomContext().rand<int>(), int{1689082725});
+ BOOST_CHECK_EQUAL(FastRandomContext().randrange<std::chrono::microseconds>(1h).count(), 2464643716);
+ BOOST_CHECK_EQUAL(FastRandomContext().randrange<std::chrono::milliseconds>(1h).count(), 2312205);
+
+ BOOST_CHECK_EQUAL(FastRandomContext().rand<uint64_t>(), uint64_t{5689404004456455543u});
+ BOOST_CHECK_EQUAL(FastRandomContext().rand<int>(), int{785839937});
+ BOOST_CHECK_EQUAL(FastRandomContext().randrange<std::chrono::microseconds>(1h).count(), 93558804);
+ BOOST_CHECK_EQUAL(FastRandomContext().randrange<std::chrono::milliseconds>(1h).count(), 507022);
}
+
{
constexpr SteadySeconds time_point{1s};
FastRandomContext ctx{true};
BOOST_CHECK_EQUAL(7, ctx.rand_uniform_delay(time_point, 9s).time_since_epoch().count());
BOOST_CHECK_EQUAL(-6, ctx.rand_uniform_delay(time_point, -9s).time_since_epoch().count());
BOOST_CHECK_EQUAL(1, ctx.rand_uniform_delay(time_point, 0s).time_since_epoch().count());
- BOOST_CHECK_EQUAL(1467825113502396065, ctx.rand_uniform_delay(time_point, 9223372036854775807s).time_since_epoch().count());
- BOOST_CHECK_EQUAL(-970181367944767837, ctx.rand_uniform_delay(time_point, -9223372036854775807s).time_since_epoch().count());
- BOOST_CHECK_EQUAL(24761, ctx.rand_uniform_delay(time_point, 9h).time_since_epoch().count());
+ BOOST_CHECK_EQUAL(4652286523065884857, ctx.rand_uniform_delay(time_point, 9223372036854775807s).time_since_epoch().count());
+ BOOST_CHECK_EQUAL(-8813961240025683129, ctx.rand_uniform_delay(time_point, -9223372036854775807s).time_since_epoch().count());
+ BOOST_CHECK_EQUAL(26443, ctx.rand_uniform_delay(time_point, 9h).time_since_epoch().count());
}
BOOST_CHECK_EQUAL(ctx1.rand32(), ctx2.rand32());
BOOST_CHECK_EQUAL(ctx1.rand32(), ctx2.rand32());
@@ -65,15 +76,28 @@ BOOST_AUTO_TEST_CASE(fastrandom_tests)
// Check with time-point type
BOOST_CHECK_EQUAL(2782, ctx.rand_uniform_duration<SteadySeconds>(9h).count());
}
+}
+BOOST_AUTO_TEST_CASE(fastrandom_tests_nondeterministic)
+{
// Check that a nondeterministic ones are not
- g_mock_deterministic_tests = false;
- for (int i = 10; i > 0; --i) {
- BOOST_CHECK(GetRand<uint64_t>() != uint64_t{10393729187455219830U});
- BOOST_CHECK(GetRand<int>() != int{769702006});
- BOOST_CHECK(GetRandMicros(std::chrono::hours{1}) != std::chrono::microseconds{2917185654});
- BOOST_CHECK(GetRandMillis(std::chrono::hours{1}) != std::chrono::milliseconds{2144374});
+ {
+ BOOST_CHECK(FastRandomContext().rand<uint64_t>() != uint64_t{9330418229102544152u});
+ BOOST_CHECK(FastRandomContext().rand<int>() != int{618925161});
+ BOOST_CHECK(FastRandomContext().randrange<std::chrono::microseconds>(1h).count() != 1271170921);
+ BOOST_CHECK(FastRandomContext().randrange<std::chrono::milliseconds>(1h).count() != 2803534);
+
+ BOOST_CHECK(FastRandomContext().rand<uint64_t>() != uint64_t{10170981140880778086u});
+ BOOST_CHECK(FastRandomContext().rand<int>() != int{1689082725});
+ BOOST_CHECK(FastRandomContext().randrange<std::chrono::microseconds>(1h).count() != 2464643716);
+ BOOST_CHECK(FastRandomContext().randrange<std::chrono::milliseconds>(1h).count() != 2312205);
+
+ BOOST_CHECK(FastRandomContext().rand<uint64_t>() != uint64_t{5689404004456455543u});
+ BOOST_CHECK(FastRandomContext().rand<int>() != int{785839937});
+ BOOST_CHECK(FastRandomContext().randrange<std::chrono::microseconds>(1h).count() != 93558804);
+ BOOST_CHECK(FastRandomContext().randrange<std::chrono::milliseconds>(1h).count() != 507022);
}
+
{
FastRandomContext ctx3, ctx4;
BOOST_CHECK(ctx3.rand64() != ctx4.rand64()); // extremely unlikely to be equal
@@ -103,6 +127,70 @@ BOOST_AUTO_TEST_CASE(fastrandom_randbits)
}
}
+/** Verify that RandomMixin::randbits returns 0 and 1 for every requested bit. */
+BOOST_AUTO_TEST_CASE(randbits_test)
+{
+ FastRandomContext ctx_lens; //!< RNG for producing the lengths requested from ctx_test.
+ FastRandomContext ctx_test1(true), ctx_test2(true); //!< The RNGs being tested.
+ int ctx_test_bitsleft{0}; //!< (Assumed value of) ctx_test::bitbuf_len
+
+ // Run the entire test 5 times.
+ for (int i = 0; i < 5; ++i) {
+ // count (first) how often it has occurred, and (second) how often it was true:
+ // - for every bit position, in every requested bits count (0 + 1 + 2 + ... + 64 = 2080)
+ // - for every value of ctx_test_bitsleft (0..63 = 64)
+ std::vector<std::pair<uint64_t, uint64_t>> seen(2080 * 64);
+ while (true) {
+ // Loop 1000 times, just to not continuously check std::all_of.
+ for (int j = 0; j < 1000; ++j) {
+ // Decide on a number of bits to request (0 through 64, inclusive; don't use randbits/randrange).
+ int bits = ctx_lens.rand64() % 65;
+ // Generate that many bits.
+ uint64_t gen = ctx_test1.randbits(bits);
+ // For certain bits counts, also test randbits<Bits> and compare.
+ uint64_t gen2;
+ if (bits == 0) {
+ gen2 = ctx_test2.randbits<0>();
+ } else if (bits == 1) {
+ gen2 = ctx_test2.randbits<1>();
+ } else if (bits == 7) {
+ gen2 = ctx_test2.randbits<7>();
+ } else if (bits == 32) {
+ gen2 = ctx_test2.randbits<32>();
+ } else if (bits == 51) {
+ gen2 = ctx_test2.randbits<51>();
+ } else if (bits == 64) {
+ gen2 = ctx_test2.randbits<64>();
+ } else {
+ gen2 = ctx_test2.randbits(bits);
+ }
+ BOOST_CHECK_EQUAL(gen, gen2);
+ // Make sure the result is in range.
+ if (bits < 64) BOOST_CHECK_EQUAL(gen >> bits, 0);
+ // Mark all the seen bits in the output.
+ for (int bit = 0; bit < bits; ++bit) {
+ int idx = bit + (bits * (bits - 1)) / 2 + 2080 * ctx_test_bitsleft;
+ seen[idx].first += 1;
+ seen[idx].second += (gen >> bit) & 1;
+ }
+ // Update ctx_test_bitself.
+ if (bits > ctx_test_bitsleft) {
+ ctx_test_bitsleft = ctx_test_bitsleft + 64 - bits;
+ } else {
+ ctx_test_bitsleft -= bits;
+ }
+ }
+ // Loop until every bit position/combination is seen 242 times.
+ if (std::all_of(seen.begin(), seen.end(), [](const auto& x) { return x.first >= 242; })) break;
+ }
+ // Check that each bit appears within 7.78 standard deviations of 50%
+ // (each will fail with P < 1/(2080 * 64 * 10^9)).
+ for (const auto& val : seen) {
+ assert(fabs(val.first * 0.5 - val.second) < sqrt(val.first * 0.25) * 7.78);
+ }
+ }
+}
+
/** Does-it-compile test for compatibility with standard library RNG interface. */
BOOST_AUTO_TEST_CASE(stdrandom_test)
{
@@ -118,10 +206,6 @@ BOOST_AUTO_TEST_CASE(stdrandom_test)
for (int j = 1; j <= 10; ++j) {
BOOST_CHECK(std::find(test.begin(), test.end(), j) != test.end());
}
- Shuffle(test.begin(), test.end(), ctx);
- for (int j = 1; j <= 10; ++j) {
- BOOST_CHECK(std::find(test.begin(), test.end(), j) != test.end());
- }
}
}
@@ -132,7 +216,7 @@ BOOST_AUTO_TEST_CASE(shuffle_stat_test)
uint32_t counts[5 * 5 * 5 * 5 * 5] = {0};
for (int i = 0; i < 12000; ++i) {
int data[5] = {0, 1, 2, 3, 4};
- Shuffle(std::begin(data), std::end(data), ctx);
+ std::shuffle(std::begin(data), std::end(data), ctx);
int pos = data[0] + data[1] * 5 + data[2] * 25 + data[3] * 125 + data[4] * 625;
++counts[pos];
}
@@ -155,4 +239,21 @@ BOOST_AUTO_TEST_CASE(shuffle_stat_test)
BOOST_CHECK_EQUAL(sum, 12000U);
}
+BOOST_AUTO_TEST_CASE(xoroshiro128plusplus_reference_values)
+{
+ // numbers generated from reference implementation
+ InsecureRandomContext rng(0);
+ BOOST_TEST(0x6f68e1e7e2646ee1 == rng());
+ BOOST_TEST(0xbf971b7f454094ad == rng());
+ BOOST_TEST(0x48f2de556f30de38 == rng());
+ BOOST_TEST(0x6ea7c59f89bbfc75 == rng());
+
+ // seed with a random number
+ rng.Reseed(0x1a26f3fa8546b47a);
+ BOOST_TEST(0xc8dc5e08d844ac7d == rng());
+ BOOST_TEST(0x5b5f1f6d499dad1b == rng());
+ BOOST_TEST(0xbeb0031f93313d6f == rng());
+ BOOST_TEST(0xbfbcf4f43a264497 == rng());
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/script_p2sh_tests.cpp b/src/test/script_p2sh_tests.cpp
index 54dcc218b9..f91203cc48 100644
--- a/src/test/script_p2sh_tests.cpp
+++ b/src/test/script_p2sh_tests.cpp
@@ -113,13 +113,14 @@ BOOST_AUTO_TEST_CASE(sign)
}
// All of the above should be OK, and the txTos have valid signatures
// Check to make sure signature verification fails if we use the wrong ScriptSig:
+ SignatureCache signature_cache{DEFAULT_SIGNATURE_CACHE_BYTES};
for (int i = 0; i < 8; i++) {
PrecomputedTransactionData txdata(txTo[i]);
for (int j = 0; j < 8; j++)
{
CScript sigSave = txTo[i].vin[0].scriptSig;
txTo[i].vin[0].scriptSig = txTo[j].vin[0].scriptSig;
- bool sigOK = CScriptCheck(txFrom.vout[txTo[i].vin[0].prevout.n], CTransaction(txTo[i]), 0, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC, false, &txdata)();
+ bool sigOK = CScriptCheck(txFrom.vout[txTo[i].vin[0].prevout.n], CTransaction(txTo[i]), signature_cache, 0, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC, false, &txdata)();
if (i == j)
BOOST_CHECK_MESSAGE(sigOK, strprintf("VerifySignature %d %d", i, j));
else
diff --git a/src/test/script_standard_tests.cpp b/src/test/script_standard_tests.cpp
index 29e2d4a569..6befd9ba85 100644
--- a/src/test/script_standard_tests.cpp
+++ b/src/test/script_standard_tests.cpp
@@ -128,6 +128,20 @@ BOOST_AUTO_TEST_CASE(script_standard_Solver_success)
BOOST_CHECK(solutions[0] == std::vector<unsigned char>{16});
BOOST_CHECK(solutions[1] == ToByteVector(uint256::ONE));
+ // TxoutType::ANCHOR
+ std::vector<unsigned char> anchor_bytes{0x4e, 0x73};
+ s.clear();
+ s << OP_1 << anchor_bytes;
+ BOOST_CHECK_EQUAL(Solver(s, solutions), TxoutType::ANCHOR);
+ BOOST_CHECK(solutions.empty());
+
+ // Sanity-check IsPayToAnchor
+ int version{-1};
+ std::vector<unsigned char> witness_program;
+ BOOST_CHECK(s.IsPayToAnchor());
+ BOOST_CHECK(s.IsWitnessProgram(version, witness_program));
+ BOOST_CHECK(CScript::IsPayToAnchor(version, witness_program));
+
// TxoutType::NONSTANDARD
s.clear();
s << OP_9 << OP_ADD << OP_11 << OP_EQUAL;
@@ -186,6 +200,18 @@ BOOST_AUTO_TEST_CASE(script_standard_Solver_failure)
s.clear();
s << OP_0 << std::vector<unsigned char>(19, 0x01);
BOOST_CHECK_EQUAL(Solver(s, solutions), TxoutType::NONSTANDARD);
+
+ // TxoutType::ANCHOR but wrong witness version
+ s.clear();
+ s << OP_2 << std::vector<unsigned char>{0x4e, 0x73};
+ BOOST_CHECK(!s.IsPayToAnchor());
+ BOOST_CHECK_EQUAL(Solver(s, solutions), TxoutType::WITNESS_UNKNOWN);
+
+ // TxoutType::ANCHOR but wrong 2-byte data push
+ s.clear();
+ s << OP_1 << std::vector<unsigned char>{0xff, 0xff};
+ BOOST_CHECK(!s.IsPayToAnchor());
+ BOOST_CHECK_EQUAL(Solver(s, solutions), TxoutType::WITNESS_UNKNOWN);
}
BOOST_AUTO_TEST_CASE(script_standard_ExtractDestination)
@@ -367,7 +393,7 @@ BOOST_AUTO_TEST_CASE(script_standard_taproot_builder)
XOnlyPubKey key_2{ParseHex("f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9")};
CScript script_1 = CScript() << ToByteVector(key_1) << OP_CHECKSIG;
CScript script_2 = CScript() << ToByteVector(key_2) << OP_CHECKSIG;
- uint256 hash_3 = uint256S("31fe7061656bea2a36aa60a2f7ef940578049273746935d296426dc0afd86b68");
+ constexpr uint256 hash_3{"31fe7061656bea2a36aa60a2f7ef940578049273746935d296426dc0afd86b68"};
TaprootBuilder builder;
BOOST_CHECK(builder.IsValid() && builder.IsComplete());
diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp
index 39b53295e7..c5a8f2dfbc 100644
--- a/src/test/script_tests.cpp
+++ b/src/test/script_tests.cpp
@@ -1277,6 +1277,19 @@ BOOST_AUTO_TEST_CASE(sign_invalid_miniscript)
BOOST_CHECK(!SignSignature(keystore, CTransaction(prev), curr, 0, SIGHASH_ALL, sig_data));
}
+/* P2A input should be considered signed. */
+BOOST_AUTO_TEST_CASE(sign_paytoanchor)
+{
+ FillableSigningProvider keystore;
+ SignatureData sig_data;
+ CMutableTransaction prev, curr;
+ prev.vout.emplace_back(0, GetScriptForDestination(PayToAnchor{}));
+
+ curr.vin.emplace_back(COutPoint{prev.GetHash(), 0});
+
+ BOOST_CHECK(SignSignature(keystore, CTransaction(prev), curr, 0, SIGHASH_ALL, sig_data));
+}
+
BOOST_AUTO_TEST_CASE(script_standard_push)
{
ScriptError err;
@@ -1526,7 +1539,7 @@ static std::vector<unsigned int> AllConsensusFlags()
/** Precomputed list of all valid combinations of consensus-relevant script validation flags. */
static const std::vector<unsigned int> ALL_CONSENSUS_FLAGS = AllConsensusFlags();
-static void AssetTest(const UniValue& test)
+static void AssetTest(const UniValue& test, SignatureCache& signature_cache)
{
BOOST_CHECK(test.isObject());
@@ -1543,7 +1556,7 @@ static void AssetTest(const UniValue& test)
CTransaction tx(mtx);
PrecomputedTransactionData txdata;
txdata.Init(tx, std::vector<CTxOut>(prevouts));
- CachingTransactionSignatureChecker txcheck(&tx, idx, prevouts[idx].nValue, true, txdata);
+ CachingTransactionSignatureChecker txcheck(&tx, idx, prevouts[idx].nValue, true, signature_cache, txdata);
for (const auto flags : ALL_CONSENSUS_FLAGS) {
// "final": true tests are valid for all flags. Others are only valid with flags that are
@@ -1561,7 +1574,7 @@ static void AssetTest(const UniValue& test)
CTransaction tx(mtx);
PrecomputedTransactionData txdata;
txdata.Init(tx, std::vector<CTxOut>(prevouts));
- CachingTransactionSignatureChecker txcheck(&tx, idx, prevouts[idx].nValue, true, txdata);
+ CachingTransactionSignatureChecker txcheck(&tx, idx, prevouts[idx].nValue, true, signature_cache, txdata);
for (const auto flags : ALL_CONSENSUS_FLAGS) {
// If a test is supposed to fail with test_flags, it should also fail with any superset thereof.
@@ -1577,6 +1590,7 @@ BOOST_AUTO_TEST_CASE(script_assets_test)
{
// See src/test/fuzz/script_assets_test_minimizer.cpp for information on how to generate
// the script_assets_test.json file used by this test.
+ SignatureCache signature_cache{DEFAULT_SIGNATURE_CACHE_BYTES};
const char* dir = std::getenv("DIR_UNIT_TEST_DATA");
BOOST_WARN_MESSAGE(dir != nullptr, "Variable DIR_UNIT_TEST_DATA unset, skipping script_assets_test");
@@ -1597,7 +1611,7 @@ BOOST_AUTO_TEST_CASE(script_assets_test)
BOOST_CHECK(tests.size() > 0);
for (size_t i = 0; i < tests.size(); i++) {
- AssetTest(tests[i]);
+ AssetTest(tests[i], signature_cache);
}
file.close();
}
@@ -1678,17 +1692,17 @@ BOOST_AUTO_TEST_CASE(bip341_keypath_test_vectors)
BOOST_AUTO_TEST_CASE(compute_tapbranch)
{
- uint256 hash1 = uint256S("8ad69ec7cf41c2a4001fd1f738bf1e505ce2277acdcaa63fe4765192497f47a7");
- uint256 hash2 = uint256S("f224a923cd0021ab202ab139cc56802ddb92dcfc172b9212261a539df79a112a");
- uint256 result = uint256S("a64c5b7b943315f9b805d7a7296bedfcfd08919270a1f7a1466e98f8693d8cd9");
+ constexpr uint256 hash1{"8ad69ec7cf41c2a4001fd1f738bf1e505ce2277acdcaa63fe4765192497f47a7"};
+ constexpr uint256 hash2{"f224a923cd0021ab202ab139cc56802ddb92dcfc172b9212261a539df79a112a"};
+ constexpr uint256 result{"a64c5b7b943315f9b805d7a7296bedfcfd08919270a1f7a1466e98f8693d8cd9"};
BOOST_CHECK_EQUAL(ComputeTapbranchHash(hash1, hash2), result);
}
BOOST_AUTO_TEST_CASE(compute_tapleaf)
{
- const uint8_t script[6] = {'f','o','o','b','a','r'};
- uint256 tlc0 = uint256S("edbc10c272a1215dcdcc11d605b9027b5ad6ed97cd45521203f136767b5b9c06");
- uint256 tlc2 = uint256S("8b5c4f90ae6bf76e259dbef5d8a59df06359c391b59263741b25eca76451b27a");
+ constexpr uint8_t script[6] = {'f','o','o','b','a','r'};
+ constexpr uint256 tlc0{"edbc10c272a1215dcdcc11d605b9027b5ad6ed97cd45521203f136767b5b9c06"};
+ constexpr uint256 tlc2{"8b5c4f90ae6bf76e259dbef5d8a59df06359c391b59263741b25eca76451b27a"};
BOOST_CHECK_EQUAL(ComputeTapleafHash(0xc0, Span(script)), tlc0);
BOOST_CHECK_EQUAL(ComputeTapleafHash(0xc2, Span(script)), tlc2);
diff --git a/src/test/serfloat_tests.cpp b/src/test/serfloat_tests.cpp
index 304541074f..51c026e2ed 100644
--- a/src/test/serfloat_tests.cpp
+++ b/src/test/serfloat_tests.cpp
@@ -139,7 +139,7 @@ BOOST_AUTO_TEST_CASE(doubles)
for (int i = 0; i < 1000; i++) {
ss << EncodeDouble(i);
}
- BOOST_CHECK(Hash(ss) == uint256S("43d0c82591953c4eafe114590d392676a01585d25b25d433557f0d7878b23f96"));
+ BOOST_CHECK(Hash(ss) == uint256{"43d0c82591953c4eafe114590d392676a01585d25b25d433557f0d7878b23f96"});
// decode
for (int i = 0; i < 1000; i++) {
diff --git a/src/test/streams_tests.cpp b/src/test/streams_tests.cpp
index e666e11758..9296cbb41c 100644
--- a/src/test/streams_tests.cpp
+++ b/src/test/streams_tests.cpp
@@ -30,8 +30,7 @@ BOOST_AUTO_TEST_CASE(xor_file)
}
{
#ifdef __MINGW64__
- // Our usage of mingw-w64 and the msvcrt runtime does not support
- // the x modifier for the _wfopen().
+ // Temporary workaround for https://github.com/bitcoin/bitcoin/issues/30210
const char* mode = "wb";
#else
const char* mode = "wbx";
@@ -436,7 +435,7 @@ BOOST_AUTO_TEST_CASE(streams_buffered_file_skip)
BOOST_AUTO_TEST_CASE(streams_buffered_file_rand)
{
// Make this test deterministic.
- SeedInsecureRand(SeedRand::ZEROS);
+ SeedRandomForTest(SeedRand::ZEROS);
fs::path streams_test_filename = m_args.GetDataDirBase() / "streams_test_tmp";
for (int rep = 0; rep < 50; ++rep) {
diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp
index 34176626f0..5622632e97 100644
--- a/src/test/transaction_tests.cpp
+++ b/src/test/transaction_tests.cpp
@@ -17,6 +17,7 @@
#include <policy/settings.h>
#include <script/script.h>
#include <script/script_error.h>
+#include <script/sigcache.h>
#include <script/sign.h>
#include <script/signingprovider.h>
#include <script/solver.h>
@@ -222,7 +223,7 @@ BOOST_AUTO_TEST_CASE(tx_valid)
fValid = false;
break;
}
- COutPoint outpoint{TxidFromString(vinput[0].get_str()), uint32_t(vinput[1].getInt<int>())};
+ COutPoint outpoint{Txid::FromHex(vinput[0].get_str()).value(), uint32_t(vinput[1].getInt<int>())};
mapprevOutScriptPubKeys[outpoint] = ParseScript(vinput[2].get_str());
if (vinput.size() >= 4)
{
@@ -310,7 +311,7 @@ BOOST_AUTO_TEST_CASE(tx_invalid)
fValid = false;
break;
}
- COutPoint outpoint{TxidFromString(vinput[0].get_str()), uint32_t(vinput[1].getInt<int>())};
+ COutPoint outpoint{Txid::FromHex(vinput[0].get_str()).value(), uint32_t(vinput[1].getInt<int>())};
mapprevOutScriptPubKeys[outpoint] = ParseScript(vinput[2].get_str());
if (vinput.size() >= 4)
{
@@ -541,7 +542,7 @@ BOOST_AUTO_TEST_CASE(test_big_witness_transaction)
// create a big transaction of 4500 inputs signed by the same key
for(uint32_t ij = 0; ij < 4500; ij++) {
uint32_t i = mtx.vin.size();
- COutPoint outpoint(TxidFromString("0000000000000000000000000000000000000000000000000000000000000100"), i);
+ COutPoint outpoint(Txid::FromHex("0000000000000000000000000000000000000000000000000000000000000100").value(), i);
mtx.vin.resize(mtx.vin.size() + 1);
mtx.vin[i].prevout = outpoint;
@@ -578,9 +579,11 @@ BOOST_AUTO_TEST_CASE(test_big_witness_transaction)
coins.emplace_back(std::move(coin));
}
+ SignatureCache signature_cache{DEFAULT_SIGNATURE_CACHE_BYTES};
+
for(uint32_t i = 0; i < mtx.vin.size(); i++) {
std::vector<CScriptCheck> vChecks;
- vChecks.emplace_back(coins[tx.vin[i].prevout.n].out, tx, i, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, false, &txdata);
+ vChecks.emplace_back(coins[tx.vin[i].prevout.n].out, tx, signature_cache, i, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, false, &txdata);
control.Add(std::move(vChecks));
}
@@ -1023,6 +1026,14 @@ BOOST_AUTO_TEST_CASE(test_IsStandard)
t.vout[0].nValue = 239;
CheckIsNotStandard(t, "dust");
}
+
+ // Check anchor outputs
+ t.vout[0].scriptPubKey = CScript() << OP_1 << std::vector<unsigned char>{0x4e, 0x73};
+ BOOST_CHECK(t.vout[0].scriptPubKey.IsPayToAnchor());
+ t.vout[0].nValue = 240;
+ CheckIsStandard(t);
+ t.vout[0].nValue = 239;
+ CheckIsNotStandard(t, "dust");
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/txpackage_tests.cpp b/src/test/txpackage_tests.cpp
index 478121cc6f..ca9dc5527d 100644
--- a/src/test/txpackage_tests.cpp
+++ b/src/test/txpackage_tests.cpp
@@ -43,13 +43,6 @@ inline CTransactionRef create_placeholder_tx(size_t num_inputs, size_t num_outpu
}
return MakeTransactionRef(mtx);
}
-
-// Create a Wtxid from a hex string
-inline Wtxid WtxidFromString(std::string_view str)
-{
- return Wtxid::FromUint256(uint256S(str.data()));
-}
-
BOOST_FIXTURE_TEST_CASE(package_hash_tests, TestChain100Setup)
{
// Random real segwit transaction
@@ -74,9 +67,9 @@ BOOST_FIXTURE_TEST_CASE(package_hash_tests, TestChain100Setup)
CTransactionRef ptx_3{MakeTransactionRef(tx_3)};
// It's easy to see that wtxids are sorted in lexicographical order:
- Wtxid wtxid_1{WtxidFromString("0x85cd1a31eb38f74ed5742ec9cb546712ab5aaf747de28a9168b53e846cbda17f")};
- Wtxid wtxid_2{WtxidFromString("0xb4749f017444b051c44dfd2720e88f314ff94f3dd6d56d40ef65854fcd7fff6b")};
- Wtxid wtxid_3{WtxidFromString("0xe065bac15f62bb4e761d761db928ddee65a47296b2b776785abb912cdec474e3")};
+ Wtxid wtxid_1{Wtxid::FromHex("85cd1a31eb38f74ed5742ec9cb546712ab5aaf747de28a9168b53e846cbda17f").value()};
+ Wtxid wtxid_2{Wtxid::FromHex("b4749f017444b051c44dfd2720e88f314ff94f3dd6d56d40ef65854fcd7fff6b").value()};
+ Wtxid wtxid_3{Wtxid::FromHex("e065bac15f62bb4e761d761db928ddee65a47296b2b776785abb912cdec474e3").value()};
BOOST_CHECK_EQUAL(tx_1.GetWitnessHash(), wtxid_1);
BOOST_CHECK_EQUAL(tx_2.GetWitnessHash(), wtxid_2);
BOOST_CHECK_EQUAL(tx_3.GetWitnessHash(), wtxid_3);
@@ -85,9 +78,9 @@ BOOST_FIXTURE_TEST_CASE(package_hash_tests, TestChain100Setup)
BOOST_CHECK(wtxid_2.GetHex() < wtxid_3.GetHex());
// The txids are not (we want to test that sorting and hashing use wtxid, not txid):
- Txid txid_1{TxidFromString("0xbd0f71c1d5e50589063e134fad22053cdae5ab2320db5bf5e540198b0b5a4e69")};
- Txid txid_2{TxidFromString("0xb4749f017444b051c44dfd2720e88f314ff94f3dd6d56d40ef65854fcd7fff6b")};
- Txid txid_3{TxidFromString("0xee707be5201160e32c4fc715bec227d1aeea5940fb4295605e7373edce3b1a93")};
+ Txid txid_1{Txid::FromHex("bd0f71c1d5e50589063e134fad22053cdae5ab2320db5bf5e540198b0b5a4e69").value()};
+ Txid txid_2{Txid::FromHex("b4749f017444b051c44dfd2720e88f314ff94f3dd6d56d40ef65854fcd7fff6b").value()};
+ Txid txid_3{Txid::FromHex("ee707be5201160e32c4fc715bec227d1aeea5940fb4295605e7373edce3b1a93").value()};
BOOST_CHECK_EQUAL(tx_1.GetHash(), txid_1);
BOOST_CHECK_EQUAL(tx_2.GetHash(), txid_2);
BOOST_CHECK_EQUAL(tx_3.GetHash(), txid_3);
@@ -303,7 +296,7 @@ BOOST_FIXTURE_TEST_CASE(noncontextual_package_tests, TestChain100Setup)
// The parents can be in any order.
FastRandomContext rng;
- Shuffle(package.begin(), package.end(), rng);
+ std::shuffle(package.begin(), package.end(), rng);
package.push_back(MakeTransactionRef(child));
PackageValidationState state;
diff --git a/src/test/txrequest_tests.cpp b/src/test/txrequest_tests.cpp
index dc257a0d51..0ca70d2c7a 100644
--- a/src/test/txrequest_tests.cpp
+++ b/src/test/txrequest_tests.cpp
@@ -392,7 +392,7 @@ void BuildBigPriorityTest(Scenario& scenario, int peers)
// Determine the announcement order randomly.
std::vector<NodeId> announce_order = request_order;
- Shuffle(announce_order.begin(), announce_order.end(), g_insecure_rand_ctx);
+ std::shuffle(announce_order.begin(), announce_order.end(), g_insecure_rand_ctx);
// Find a gtxid whose txhash prioritization is consistent with the required ordering within pref_peers and
// within npref_peers.
@@ -697,7 +697,7 @@ void TestInterleavedScenarios()
builders.emplace_back([](Scenario& scenario){ BuildWeirdRequestsTest(scenario); });
}
// Randomly shuffle all those functions.
- Shuffle(builders.begin(), builders.end(), g_insecure_rand_ctx);
+ std::shuffle(builders.begin(), builders.end(), g_insecure_rand_ctx);
Runner runner;
auto starttime = RandomTime1y();
diff --git a/src/test/txvalidationcache_tests.cpp b/src/test/txvalidationcache_tests.cpp
index 78ef96a15d..af36a95693 100644
--- a/src/test/txvalidationcache_tests.cpp
+++ b/src/test/txvalidationcache_tests.cpp
@@ -5,6 +5,7 @@
#include <consensus/validation.h>
#include <key.h>
#include <random.h>
+#include <script/sigcache.h>
#include <script/sign.h>
#include <script/signingprovider.h>
#include <test/util/setup_common.h>
@@ -16,12 +17,13 @@
struct Dersig100Setup : public TestChain100Setup {
Dersig100Setup()
- : TestChain100Setup{ChainType::REGTEST, {"-testactivationheight=dersig@102"}} {}
+ : TestChain100Setup{ChainType::REGTEST, {.extra_args = {"-testactivationheight=dersig@102"}}} {}
};
bool CheckInputScripts(const CTransaction& tx, TxValidationState& state,
const CCoinsViewCache& inputs, unsigned int flags, bool cacheSigStore,
bool cacheFullScriptStore, PrecomputedTransactionData& txdata,
+ ValidationCache& validation_cache,
std::vector<CScriptCheck>* pvChecks) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
BOOST_AUTO_TEST_SUITE(txvalidationcache_tests)
@@ -118,7 +120,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, Dersig100Setup)
// should fail.
// Capture this interaction with the upgraded_nop argument: set it when evaluating
// any script flag that is implemented as an upgraded NOP code.
-static void ValidateCheckInputsForAllFlags(const CTransaction &tx, uint32_t failing_flags, bool add_to_cache, CCoinsViewCache& active_coins_tip) EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
+static void ValidateCheckInputsForAllFlags(const CTransaction &tx, uint32_t failing_flags, bool add_to_cache, CCoinsViewCache& active_coins_tip, ValidationCache& validation_cache) EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
{
PrecomputedTransactionData txdata;
@@ -140,7 +142,7 @@ static void ValidateCheckInputsForAllFlags(const CTransaction &tx, uint32_t fail
// WITNESS requires P2SH
test_flags |= SCRIPT_VERIFY_P2SH;
}
- bool ret = CheckInputScripts(tx, state, &active_coins_tip, test_flags, true, add_to_cache, txdata, nullptr);
+ bool ret = CheckInputScripts(tx, state, &active_coins_tip, test_flags, true, add_to_cache, txdata, validation_cache, nullptr);
// CheckInputScripts should succeed iff test_flags doesn't intersect with
// failing_flags
bool expected_return_value = !(test_flags & failing_flags);
@@ -150,13 +152,13 @@ static void ValidateCheckInputsForAllFlags(const CTransaction &tx, uint32_t fail
if (ret && add_to_cache) {
// Check that we get a cache hit if the tx was valid
std::vector<CScriptCheck> scriptchecks;
- BOOST_CHECK(CheckInputScripts(tx, state, &active_coins_tip, test_flags, true, add_to_cache, txdata, &scriptchecks));
+ BOOST_CHECK(CheckInputScripts(tx, state, &active_coins_tip, test_flags, true, add_to_cache, txdata, validation_cache, &scriptchecks));
BOOST_CHECK(scriptchecks.empty());
} else {
// Check that we get script executions to check, if the transaction
// was invalid, or we didn't add to cache.
std::vector<CScriptCheck> scriptchecks;
- BOOST_CHECK(CheckInputScripts(tx, state, &active_coins_tip, test_flags, true, add_to_cache, txdata, &scriptchecks));
+ BOOST_CHECK(CheckInputScripts(tx, state, &active_coins_tip, test_flags, true, add_to_cache, txdata, validation_cache, &scriptchecks));
BOOST_CHECK_EQUAL(scriptchecks.size(), tx.vin.size());
}
}
@@ -214,20 +216,20 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, Dersig100Setup)
TxValidationState state;
PrecomputedTransactionData ptd_spend_tx;
- BOOST_CHECK(!CheckInputScripts(CTransaction(spend_tx), state, &m_node.chainman->ActiveChainstate().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, nullptr));
+ BOOST_CHECK(!CheckInputScripts(CTransaction(spend_tx), state, &m_node.chainman->ActiveChainstate().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, m_node.chainman->m_validation_cache, nullptr));
// If we call again asking for scriptchecks (as happens in
// ConnectBlock), we should add a script check object for this -- we're
// not caching invalidity (if that changes, delete this test case).
std::vector<CScriptCheck> scriptchecks;
- BOOST_CHECK(CheckInputScripts(CTransaction(spend_tx), state, &m_node.chainman->ActiveChainstate().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, &scriptchecks));
+ BOOST_CHECK(CheckInputScripts(CTransaction(spend_tx), state, &m_node.chainman->ActiveChainstate().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, m_node.chainman->m_validation_cache, &scriptchecks));
BOOST_CHECK_EQUAL(scriptchecks.size(), 1U);
// Test that CheckInputScripts returns true iff DERSIG-enforcing flags are
// not present. Don't add these checks to the cache, so that we can
// test later that block validation works fine in the absence of cached
// successes.
- ValidateCheckInputsForAllFlags(CTransaction(spend_tx), SCRIPT_VERIFY_DERSIG | SCRIPT_VERIFY_LOW_S | SCRIPT_VERIFY_STRICTENC, false, m_node.chainman->ActiveChainstate().CoinsTip());
+ ValidateCheckInputsForAllFlags(CTransaction(spend_tx), SCRIPT_VERIFY_DERSIG | SCRIPT_VERIFY_LOW_S | SCRIPT_VERIFY_STRICTENC, false, m_node.chainman->ActiveChainstate().CoinsTip(), m_node.chainman->m_validation_cache);
}
// And if we produce a block with this tx, it should be valid (DERSIG not
@@ -253,7 +255,7 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, Dersig100Setup)
std::vector<unsigned char> vchSig2(p2pk_scriptPubKey.begin(), p2pk_scriptPubKey.end());
invalid_under_p2sh_tx.vin[0].scriptSig << vchSig2;
- ValidateCheckInputsForAllFlags(CTransaction(invalid_under_p2sh_tx), SCRIPT_VERIFY_P2SH, true, m_node.chainman->ActiveChainstate().CoinsTip());
+ ValidateCheckInputsForAllFlags(CTransaction(invalid_under_p2sh_tx), SCRIPT_VERIFY_P2SH, true, m_node.chainman->ActiveChainstate().CoinsTip(), m_node.chainman->m_validation_cache);
}
// Test CHECKLOCKTIMEVERIFY
@@ -276,13 +278,13 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, Dersig100Setup)
vchSig.push_back((unsigned char)SIGHASH_ALL);
invalid_with_cltv_tx.vin[0].scriptSig = CScript() << vchSig << 101;
- ValidateCheckInputsForAllFlags(CTransaction(invalid_with_cltv_tx), SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, true, m_node.chainman->ActiveChainstate().CoinsTip());
+ ValidateCheckInputsForAllFlags(CTransaction(invalid_with_cltv_tx), SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, true, m_node.chainman->ActiveChainstate().CoinsTip(), m_node.chainman->m_validation_cache);
// Make it valid, and check again
invalid_with_cltv_tx.vin[0].scriptSig = CScript() << vchSig << 100;
TxValidationState state;
PrecomputedTransactionData txdata;
- BOOST_CHECK(CheckInputScripts(CTransaction(invalid_with_cltv_tx), state, m_node.chainman->ActiveChainstate().CoinsTip(), SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, true, true, txdata, nullptr));
+ BOOST_CHECK(CheckInputScripts(CTransaction(invalid_with_cltv_tx), state, m_node.chainman->ActiveChainstate().CoinsTip(), SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, true, true, txdata, m_node.chainman->m_validation_cache, nullptr));
}
// TEST CHECKSEQUENCEVERIFY
@@ -304,13 +306,13 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, Dersig100Setup)
vchSig.push_back((unsigned char)SIGHASH_ALL);
invalid_with_csv_tx.vin[0].scriptSig = CScript() << vchSig << 101;
- ValidateCheckInputsForAllFlags(CTransaction(invalid_with_csv_tx), SCRIPT_VERIFY_CHECKSEQUENCEVERIFY, true, m_node.chainman->ActiveChainstate().CoinsTip());
+ ValidateCheckInputsForAllFlags(CTransaction(invalid_with_csv_tx), SCRIPT_VERIFY_CHECKSEQUENCEVERIFY, true, m_node.chainman->ActiveChainstate().CoinsTip(), m_node.chainman->m_validation_cache);
// Make it valid, and check again
invalid_with_csv_tx.vin[0].scriptSig = CScript() << vchSig << 100;
TxValidationState state;
PrecomputedTransactionData txdata;
- BOOST_CHECK(CheckInputScripts(CTransaction(invalid_with_csv_tx), state, &m_node.chainman->ActiveChainstate().CoinsTip(), SCRIPT_VERIFY_CHECKSEQUENCEVERIFY, true, true, txdata, nullptr));
+ BOOST_CHECK(CheckInputScripts(CTransaction(invalid_with_csv_tx), state, &m_node.chainman->ActiveChainstate().CoinsTip(), SCRIPT_VERIFY_CHECKSEQUENCEVERIFY, true, true, txdata, m_node.chainman->m_validation_cache, nullptr));
}
// TODO: add tests for remaining script flags
@@ -333,11 +335,11 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, Dersig100Setup)
UpdateInput(valid_with_witness_tx.vin[0], sigdata);
// This should be valid under all script flags.
- ValidateCheckInputsForAllFlags(CTransaction(valid_with_witness_tx), 0, true, m_node.chainman->ActiveChainstate().CoinsTip());
+ ValidateCheckInputsForAllFlags(CTransaction(valid_with_witness_tx), 0, true, m_node.chainman->ActiveChainstate().CoinsTip(), m_node.chainman->m_validation_cache);
// Remove the witness, and check that it is now invalid.
valid_with_witness_tx.vin[0].scriptWitness.SetNull();
- ValidateCheckInputsForAllFlags(CTransaction(valid_with_witness_tx), SCRIPT_VERIFY_WITNESS, true, m_node.chainman->ActiveChainstate().CoinsTip());
+ ValidateCheckInputsForAllFlags(CTransaction(valid_with_witness_tx), SCRIPT_VERIFY_WITNESS, true, m_node.chainman->ActiveChainstate().CoinsTip(), m_node.chainman->m_validation_cache);
}
{
@@ -362,7 +364,7 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, Dersig100Setup)
}
// This should be valid under all script flags
- ValidateCheckInputsForAllFlags(CTransaction(tx), 0, true, m_node.chainman->ActiveChainstate().CoinsTip());
+ ValidateCheckInputsForAllFlags(CTransaction(tx), 0, true, m_node.chainman->ActiveChainstate().CoinsTip(), m_node.chainman->m_validation_cache);
// Check that if the second input is invalid, but the first input is
// valid, the transaction is not cached.
@@ -372,12 +374,12 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, Dersig100Setup)
TxValidationState state;
PrecomputedTransactionData txdata;
// This transaction is now invalid under segwit, because of the second input.
- BOOST_CHECK(!CheckInputScripts(CTransaction(tx), state, &m_node.chainman->ActiveChainstate().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true, true, txdata, nullptr));
+ BOOST_CHECK(!CheckInputScripts(CTransaction(tx), state, &m_node.chainman->ActiveChainstate().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true, true, txdata, m_node.chainman->m_validation_cache, nullptr));
std::vector<CScriptCheck> scriptchecks;
// Make sure this transaction was not cached (ie because the first
// input was valid)
- BOOST_CHECK(CheckInputScripts(CTransaction(tx), state, &m_node.chainman->ActiveChainstate().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true, true, txdata, &scriptchecks));
+ BOOST_CHECK(CheckInputScripts(CTransaction(tx), state, &m_node.chainman->ActiveChainstate().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true, true, txdata, m_node.chainman->m_validation_cache, &scriptchecks));
// Should get 2 script checks back -- caching is on a whole-transaction basis.
BOOST_CHECK_EQUAL(scriptchecks.size(), 2U);
}
diff --git a/src/test/uint256_tests.cpp b/src/test/uint256_tests.cpp
index b7892a2139..2ba3227174 100644
--- a/src/test/uint256_tests.cpp
+++ b/src/test/uint256_tests.cpp
@@ -6,12 +6,15 @@
#include <streams.h>
#include <test/util/setup_common.h>
#include <uint256.h>
+#include <util/strencodings.h>
+#include <util/transaction_identifier.h>
#include <boost/test/unit_test.hpp>
#include <iomanip>
#include <sstream>
#include <string>
+#include <string_view>
#include <vector>
BOOST_AUTO_TEST_SUITE(uint256_tests)
@@ -58,69 +61,68 @@ static std::string ArrayToString(const unsigned char A[], unsigned int width)
return Stream.str();
}
-inline uint160 uint160S(const char *str)
+// Takes hex string in reverse byte order.
+inline uint160 uint160S(std::string_view str)
{
uint160 rv;
- rv.SetHex(str);
- return rv;
-}
-inline uint160 uint160S(const std::string& str)
-{
- uint160 rv;
- rv.SetHex(str);
+ rv.SetHexDeprecated(str);
return rv;
}
BOOST_AUTO_TEST_CASE( basics ) // constructors, equality, inequality
{
- BOOST_CHECK(1 == 0+1);
// constructor uint256(vector<char>):
- BOOST_CHECK(R1L.ToString() == ArrayToString(R1Array,32));
- BOOST_CHECK(R1S.ToString() == ArrayToString(R1Array,20));
- BOOST_CHECK(R2L.ToString() == ArrayToString(R2Array,32));
- BOOST_CHECK(R2S.ToString() == ArrayToString(R2Array,20));
- BOOST_CHECK(ZeroL.ToString() == ArrayToString(ZeroArray,32));
- BOOST_CHECK(ZeroS.ToString() == ArrayToString(ZeroArray,20));
- BOOST_CHECK(OneL.ToString() == ArrayToString(OneArray,32));
- BOOST_CHECK(OneS.ToString() == ArrayToString(OneArray,20));
- BOOST_CHECK(MaxL.ToString() == ArrayToString(MaxArray,32));
- BOOST_CHECK(MaxS.ToString() == ArrayToString(MaxArray,20));
- BOOST_CHECK(OneL.ToString() != ArrayToString(ZeroArray,32));
- BOOST_CHECK(OneS.ToString() != ArrayToString(ZeroArray,20));
+ BOOST_CHECK_EQUAL(R1L.ToString(), ArrayToString(R1Array,32));
+ BOOST_CHECK_EQUAL(R1S.ToString(), ArrayToString(R1Array,20));
+ BOOST_CHECK_EQUAL(R2L.ToString(), ArrayToString(R2Array,32));
+ BOOST_CHECK_EQUAL(R2S.ToString(), ArrayToString(R2Array,20));
+ BOOST_CHECK_EQUAL(ZeroL.ToString(), ArrayToString(ZeroArray,32));
+ BOOST_CHECK_EQUAL(ZeroS.ToString(), ArrayToString(ZeroArray,20));
+ BOOST_CHECK_EQUAL(OneL.ToString(), ArrayToString(OneArray,32));
+ BOOST_CHECK_EQUAL(OneS.ToString(), ArrayToString(OneArray,20));
+ BOOST_CHECK_EQUAL(MaxL.ToString(), ArrayToString(MaxArray,32));
+ BOOST_CHECK_EQUAL(MaxS.ToString(), ArrayToString(MaxArray,20));
+ BOOST_CHECK_NE(OneL.ToString(), ArrayToString(ZeroArray,32));
+ BOOST_CHECK_NE(OneS.ToString(), ArrayToString(ZeroArray,20));
// == and !=
- BOOST_CHECK(R1L != R2L && R1S != R2S);
- BOOST_CHECK(ZeroL != OneL && ZeroS != OneS);
- BOOST_CHECK(OneL != ZeroL && OneS != ZeroS);
- BOOST_CHECK(MaxL != ZeroL && MaxS != ZeroS);
+ BOOST_CHECK_NE(R1L, R2L); BOOST_CHECK_NE(R1S, R2S);
+ BOOST_CHECK_NE(ZeroL, OneL); BOOST_CHECK_NE(ZeroS, OneS);
+ BOOST_CHECK_NE(OneL, ZeroL); BOOST_CHECK_NE(OneS, ZeroS);
+ BOOST_CHECK_NE(MaxL, ZeroL); BOOST_CHECK_NE(MaxS, ZeroS);
// String Constructor and Copy Constructor
- BOOST_CHECK(uint256S("0x"+R1L.ToString()) == R1L);
- BOOST_CHECK(uint256S("0x"+R2L.ToString()) == R2L);
- BOOST_CHECK(uint256S("0x"+ZeroL.ToString()) == ZeroL);
- BOOST_CHECK(uint256S("0x"+OneL.ToString()) == OneL);
- BOOST_CHECK(uint256S("0x"+MaxL.ToString()) == MaxL);
- BOOST_CHECK(uint256S(R1L.ToString()) == R1L);
- BOOST_CHECK(uint256S(" 0x"+R1L.ToString()+" ") == R1L);
- BOOST_CHECK(uint256S("") == ZeroL);
- BOOST_CHECK(R1L == uint256S(R1ArrayHex));
- BOOST_CHECK(uint256(R1L) == R1L);
- BOOST_CHECK(uint256(ZeroL) == ZeroL);
- BOOST_CHECK(uint256(OneL) == OneL);
-
- BOOST_CHECK(uint160S("0x"+R1S.ToString()) == R1S);
- BOOST_CHECK(uint160S("0x"+R2S.ToString()) == R2S);
- BOOST_CHECK(uint160S("0x"+ZeroS.ToString()) == ZeroS);
- BOOST_CHECK(uint160S("0x"+OneS.ToString()) == OneS);
- BOOST_CHECK(uint160S("0x"+MaxS.ToString()) == MaxS);
- BOOST_CHECK(uint160S(R1S.ToString()) == R1S);
- BOOST_CHECK(uint160S(" 0x"+R1S.ToString()+" ") == R1S);
- BOOST_CHECK(uint160S("") == ZeroS);
- BOOST_CHECK(R1S == uint160S(R1ArrayHex));
-
- BOOST_CHECK(uint160(R1S) == R1S);
- BOOST_CHECK(uint160(ZeroS) == ZeroS);
- BOOST_CHECK(uint160(OneS) == OneS);
+ BOOST_CHECK_EQUAL(uint256S("0x"+R1L.ToString()), R1L);
+ BOOST_CHECK_EQUAL(uint256S("0x"+R2L.ToString()), R2L);
+ BOOST_CHECK_EQUAL(uint256S("0x"+ZeroL.ToString()), ZeroL);
+ BOOST_CHECK_EQUAL(uint256S("0x"+OneL.ToString()), OneL);
+ BOOST_CHECK_EQUAL(uint256S("0x"+MaxL.ToString()), MaxL);
+ BOOST_CHECK_EQUAL(uint256S(R1L.ToString()), R1L);
+ BOOST_CHECK_EQUAL(uint256S(" 0x"+R1L.ToString()+" "), R1L);
+ BOOST_CHECK_EQUAL(uint256S(" 0x"+R1L.ToString()+"-trash;%^& "), R1L);
+ BOOST_CHECK_EQUAL(uint256S("\t \n \n \f\n\r\t\v\t 0x"+R1L.ToString()+" \t \n \n \f\n\r\t\v\t "), R1L);
+ BOOST_CHECK_EQUAL(uint256S(""), ZeroL);
+ BOOST_CHECK_EQUAL(uint256S("1"), OneL);
+ BOOST_CHECK_EQUAL(R1L, uint256S(R1ArrayHex));
+ BOOST_CHECK_EQUAL(uint256(R1L), R1L);
+ BOOST_CHECK_EQUAL(uint256(ZeroL), ZeroL);
+ BOOST_CHECK_EQUAL(uint256(OneL), OneL);
+
+ BOOST_CHECK_EQUAL(uint160S("0x"+R1S.ToString()), R1S);
+ BOOST_CHECK_EQUAL(uint160S("0x"+R2S.ToString()), R2S);
+ BOOST_CHECK_EQUAL(uint160S("0x"+ZeroS.ToString()), ZeroS);
+ BOOST_CHECK_EQUAL(uint160S("0x"+OneS.ToString()), OneS);
+ BOOST_CHECK_EQUAL(uint160S("0x"+MaxS.ToString()), MaxS);
+ BOOST_CHECK_EQUAL(uint160S(R1S.ToString()), R1S);
+ BOOST_CHECK_EQUAL(uint160S(" 0x"+R1S.ToString()+" "), R1S);
+ BOOST_CHECK_EQUAL(uint160S(" 0x"+R1S.ToString()+"-trash;%^& "), R1S);
+ BOOST_CHECK_EQUAL(uint160S(" \t \n \n \f\n\r\t\v\t 0x"+R1S.ToString()+" \t \n \n \f\n\r\t\v\t"), R1S);
+ BOOST_CHECK_EQUAL(uint160S(""), ZeroS);
+ BOOST_CHECK_EQUAL(R1S, uint160S(R1ArrayHex));
+
+ BOOST_CHECK_EQUAL(uint160(R1S), R1S);
+ BOOST_CHECK_EQUAL(uint160(ZeroS), ZeroS);
+ BOOST_CHECK_EQUAL(uint160(OneS), OneS);
}
BOOST_AUTO_TEST_CASE( comparison ) // <= >= < >
@@ -129,140 +131,153 @@ BOOST_AUTO_TEST_CASE( comparison ) // <= >= < >
for (int i = 255; i >= 0; --i) {
uint256 TmpL;
*(TmpL.begin() + (i>>3)) |= 1<<(7-(i&7));
- BOOST_CHECK( LastL < TmpL );
+ BOOST_CHECK_LT(LastL, TmpL);
LastL = TmpL;
}
- BOOST_CHECK( ZeroL < R1L );
- BOOST_CHECK( R2L < R1L );
- BOOST_CHECK( ZeroL < OneL );
- BOOST_CHECK( OneL < MaxL );
- BOOST_CHECK( R1L < MaxL );
- BOOST_CHECK( R2L < MaxL );
+ BOOST_CHECK_LT(ZeroL, R1L);
+ BOOST_CHECK_LT(R2L, R1L);
+ BOOST_CHECK_LT(ZeroL, OneL);
+ BOOST_CHECK_LT(OneL, MaxL);
+ BOOST_CHECK_LT(R1L, MaxL);
+ BOOST_CHECK_LT(R2L, MaxL);
uint160 LastS;
for (int i = 159; i >= 0; --i) {
uint160 TmpS;
*(TmpS.begin() + (i>>3)) |= 1<<(7-(i&7));
- BOOST_CHECK( LastS < TmpS );
+ BOOST_CHECK_LT(LastS, TmpS);
LastS = TmpS;
}
- BOOST_CHECK( ZeroS < R1S );
- BOOST_CHECK( R2S < R1S );
- BOOST_CHECK( ZeroS < OneS );
- BOOST_CHECK( OneS < MaxS );
- BOOST_CHECK( R1S < MaxS );
- BOOST_CHECK( R2S < MaxS );
+ BOOST_CHECK_LT(ZeroS, R1S);
+ BOOST_CHECK_LT(R2S, R1S);
+ BOOST_CHECK_LT(ZeroS, OneS);
+ BOOST_CHECK_LT(OneS, MaxS);
+ BOOST_CHECK_LT(R1S, MaxS);
+ BOOST_CHECK_LT(R2S, MaxS);
+
+ // Non-arithmetic uint256s compare from the beginning of their inner arrays:
+ BOOST_CHECK_LT(R2L, R1L);
+ // Ensure first element comparisons give the same order as above:
+ BOOST_CHECK_LT(*R2L.begin(), *R1L.begin());
+ // Ensure last element comparisons give a different result (swapped params):
+ BOOST_CHECK_LT(*(R1L.end()-1), *(R2L.end()-1));
+ // Hex strings represent reverse-encoded bytes, with lexicographic ordering:
+ BOOST_CHECK_LT(uint256{"1000000000000000000000000000000000000000000000000000000000000000"},
+ uint256{"0000000000000000000000000000000000000000000000000000000000000001"});
}
-BOOST_AUTO_TEST_CASE( methods ) // GetHex SetHex begin() end() size() GetLow64 GetSerializeSize, Serialize, Unserialize
+BOOST_AUTO_TEST_CASE(methods) // GetHex SetHexDeprecated FromHex begin() end() size() GetLow64 GetSerializeSize, Serialize, Unserialize
{
- BOOST_CHECK(R1L.GetHex() == R1L.ToString());
- BOOST_CHECK(R2L.GetHex() == R2L.ToString());
- BOOST_CHECK(OneL.GetHex() == OneL.ToString());
- BOOST_CHECK(MaxL.GetHex() == MaxL.ToString());
+ BOOST_CHECK_EQUAL(R1L.GetHex(), R1L.ToString());
+ BOOST_CHECK_EQUAL(R2L.GetHex(), R2L.ToString());
+ BOOST_CHECK_EQUAL(OneL.GetHex(), OneL.ToString());
+ BOOST_CHECK_EQUAL(MaxL.GetHex(), MaxL.ToString());
uint256 TmpL(R1L);
- BOOST_CHECK(TmpL == R1L);
- TmpL.SetHex(R2L.ToString()); BOOST_CHECK(TmpL == R2L);
- TmpL.SetHex(ZeroL.ToString()); BOOST_CHECK(TmpL == uint256());
-
- TmpL.SetHex(R1L.ToString());
- BOOST_CHECK(memcmp(R1L.begin(), R1Array, 32)==0);
- BOOST_CHECK(memcmp(TmpL.begin(), R1Array, 32)==0);
- BOOST_CHECK(memcmp(R2L.begin(), R2Array, 32)==0);
- BOOST_CHECK(memcmp(ZeroL.begin(), ZeroArray, 32)==0);
- BOOST_CHECK(memcmp(OneL.begin(), OneArray, 32)==0);
- BOOST_CHECK(R1L.size() == sizeof(R1L));
- BOOST_CHECK(sizeof(R1L) == 32);
- BOOST_CHECK(R1L.size() == 32);
- BOOST_CHECK(R2L.size() == 32);
- BOOST_CHECK(ZeroL.size() == 32);
- BOOST_CHECK(MaxL.size() == 32);
- BOOST_CHECK(R1L.begin() + 32 == R1L.end());
- BOOST_CHECK(R2L.begin() + 32 == R2L.end());
- BOOST_CHECK(OneL.begin() + 32 == OneL.end());
- BOOST_CHECK(MaxL.begin() + 32 == MaxL.end());
- BOOST_CHECK(TmpL.begin() + 32 == TmpL.end());
- BOOST_CHECK(GetSerializeSize(R1L) == 32);
- BOOST_CHECK(GetSerializeSize(ZeroL) == 32);
+ BOOST_CHECK_EQUAL(TmpL, R1L);
+ // Verify previous values don't persist when setting to truncated string.
+ TmpL.SetHexDeprecated("21");
+ BOOST_CHECK_EQUAL(TmpL.ToString(), "0000000000000000000000000000000000000000000000000000000000000021");
+ BOOST_CHECK_EQUAL(uint256::FromHex(R2L.ToString()).value(), R2L);
+ BOOST_CHECK_EQUAL(uint256::FromHex(ZeroL.ToString()).value(), uint256());
+
+ TmpL = uint256::FromHex(R1L.ToString()).value();
+ BOOST_CHECK_EQUAL_COLLECTIONS(R1L.begin(), R1L.end(), R1Array, R1Array + uint256::size());
+ BOOST_CHECK_EQUAL_COLLECTIONS(TmpL.begin(), TmpL.end(), R1Array, R1Array + uint256::size());
+ BOOST_CHECK_EQUAL_COLLECTIONS(R2L.begin(), R2L.end(), R2Array, R2Array + uint256::size());
+ BOOST_CHECK_EQUAL_COLLECTIONS(ZeroL.begin(), ZeroL.end(), ZeroArray, ZeroArray + uint256::size());
+ BOOST_CHECK_EQUAL_COLLECTIONS(OneL.begin(), OneL.end(), OneArray, OneArray + uint256::size());
+ BOOST_CHECK_EQUAL(R1L.size(), sizeof(R1L));
+ BOOST_CHECK_EQUAL(sizeof(R1L), 32);
+ BOOST_CHECK_EQUAL(R1L.size(), 32);
+ BOOST_CHECK_EQUAL(R2L.size(), 32);
+ BOOST_CHECK_EQUAL(ZeroL.size(), 32);
+ BOOST_CHECK_EQUAL(MaxL.size(), 32);
+ BOOST_CHECK_EQUAL(R1L.begin() + 32, R1L.end());
+ BOOST_CHECK_EQUAL(R2L.begin() + 32, R2L.end());
+ BOOST_CHECK_EQUAL(OneL.begin() + 32, OneL.end());
+ BOOST_CHECK_EQUAL(MaxL.begin() + 32, MaxL.end());
+ BOOST_CHECK_EQUAL(TmpL.begin() + 32, TmpL.end());
+ BOOST_CHECK_EQUAL(GetSerializeSize(R1L), 32);
+ BOOST_CHECK_EQUAL(GetSerializeSize(ZeroL), 32);
DataStream ss{};
ss << R1L;
- BOOST_CHECK(ss.str() == std::string(R1Array,R1Array+32));
+ BOOST_CHECK_EQUAL(ss.str(), std::string(R1Array,R1Array+32));
ss >> TmpL;
- BOOST_CHECK(R1L == TmpL);
+ BOOST_CHECK_EQUAL(R1L, TmpL);
ss.clear();
ss << ZeroL;
- BOOST_CHECK(ss.str() == std::string(ZeroArray,ZeroArray+32));
+ BOOST_CHECK_EQUAL(ss.str(), std::string(ZeroArray,ZeroArray+32));
ss >> TmpL;
- BOOST_CHECK(ZeroL == TmpL);
+ BOOST_CHECK_EQUAL(ZeroL, TmpL);
ss.clear();
ss << MaxL;
- BOOST_CHECK(ss.str() == std::string(MaxArray,MaxArray+32));
+ BOOST_CHECK_EQUAL(ss.str(), std::string(MaxArray,MaxArray+32));
ss >> TmpL;
- BOOST_CHECK(MaxL == TmpL);
+ BOOST_CHECK_EQUAL(MaxL, TmpL);
ss.clear();
- BOOST_CHECK(R1S.GetHex() == R1S.ToString());
- BOOST_CHECK(R2S.GetHex() == R2S.ToString());
- BOOST_CHECK(OneS.GetHex() == OneS.ToString());
- BOOST_CHECK(MaxS.GetHex() == MaxS.ToString());
+ BOOST_CHECK_EQUAL(R1S.GetHex(), R1S.ToString());
+ BOOST_CHECK_EQUAL(R2S.GetHex(), R2S.ToString());
+ BOOST_CHECK_EQUAL(OneS.GetHex(), OneS.ToString());
+ BOOST_CHECK_EQUAL(MaxS.GetHex(), MaxS.ToString());
uint160 TmpS(R1S);
- BOOST_CHECK(TmpS == R1S);
- TmpS.SetHex(R2S.ToString()); BOOST_CHECK(TmpS == R2S);
- TmpS.SetHex(ZeroS.ToString()); BOOST_CHECK(TmpS == uint160());
-
- TmpS.SetHex(R1S.ToString());
- BOOST_CHECK(memcmp(R1S.begin(), R1Array, 20)==0);
- BOOST_CHECK(memcmp(TmpS.begin(), R1Array, 20)==0);
- BOOST_CHECK(memcmp(R2S.begin(), R2Array, 20)==0);
- BOOST_CHECK(memcmp(ZeroS.begin(), ZeroArray, 20)==0);
- BOOST_CHECK(memcmp(OneS.begin(), OneArray, 20)==0);
- BOOST_CHECK(R1S.size() == sizeof(R1S));
- BOOST_CHECK(sizeof(R1S) == 20);
- BOOST_CHECK(R1S.size() == 20);
- BOOST_CHECK(R2S.size() == 20);
- BOOST_CHECK(ZeroS.size() == 20);
- BOOST_CHECK(MaxS.size() == 20);
- BOOST_CHECK(R1S.begin() + 20 == R1S.end());
- BOOST_CHECK(R2S.begin() + 20 == R2S.end());
- BOOST_CHECK(OneS.begin() + 20 == OneS.end());
- BOOST_CHECK(MaxS.begin() + 20 == MaxS.end());
- BOOST_CHECK(TmpS.begin() + 20 == TmpS.end());
- BOOST_CHECK(GetSerializeSize(R1S) == 20);
- BOOST_CHECK(GetSerializeSize(ZeroS) == 20);
+ BOOST_CHECK_EQUAL(TmpS, R1S);
+ BOOST_CHECK_EQUAL(uint160::FromHex(R2S.ToString()).value(), R2S);
+ BOOST_CHECK_EQUAL(uint160::FromHex(ZeroS.ToString()).value(), uint160());
+
+ TmpS = uint160::FromHex(R1S.ToString()).value();
+ BOOST_CHECK_EQUAL_COLLECTIONS(R1S.begin(), R1S.end(), R1Array, R1Array + uint160::size());
+ BOOST_CHECK_EQUAL_COLLECTIONS(TmpS.begin(), TmpS.end(), R1Array, R1Array + uint160::size());
+ BOOST_CHECK_EQUAL_COLLECTIONS(R2S.begin(), R2S.end(), R2Array, R2Array + uint160::size());
+ BOOST_CHECK_EQUAL_COLLECTIONS(ZeroS.begin(), ZeroS.end(), ZeroArray, ZeroArray + uint160::size());
+ BOOST_CHECK_EQUAL_COLLECTIONS(OneS.begin(), OneS.end(), OneArray, OneArray + uint160::size());
+ BOOST_CHECK_EQUAL(R1S.size(), sizeof(R1S));
+ BOOST_CHECK_EQUAL(sizeof(R1S), 20);
+ BOOST_CHECK_EQUAL(R1S.size(), 20);
+ BOOST_CHECK_EQUAL(R2S.size(), 20);
+ BOOST_CHECK_EQUAL(ZeroS.size(), 20);
+ BOOST_CHECK_EQUAL(MaxS.size(), 20);
+ BOOST_CHECK_EQUAL(R1S.begin() + 20, R1S.end());
+ BOOST_CHECK_EQUAL(R2S.begin() + 20, R2S.end());
+ BOOST_CHECK_EQUAL(OneS.begin() + 20, OneS.end());
+ BOOST_CHECK_EQUAL(MaxS.begin() + 20, MaxS.end());
+ BOOST_CHECK_EQUAL(TmpS.begin() + 20, TmpS.end());
+ BOOST_CHECK_EQUAL(GetSerializeSize(R1S), 20);
+ BOOST_CHECK_EQUAL(GetSerializeSize(ZeroS), 20);
ss << R1S;
- BOOST_CHECK(ss.str() == std::string(R1Array,R1Array+20));
+ BOOST_CHECK_EQUAL(ss.str(), std::string(R1Array,R1Array+20));
ss >> TmpS;
- BOOST_CHECK(R1S == TmpS);
+ BOOST_CHECK_EQUAL(R1S, TmpS);
ss.clear();
ss << ZeroS;
- BOOST_CHECK(ss.str() == std::string(ZeroArray,ZeroArray+20));
+ BOOST_CHECK_EQUAL(ss.str(), std::string(ZeroArray,ZeroArray+20));
ss >> TmpS;
- BOOST_CHECK(ZeroS == TmpS);
+ BOOST_CHECK_EQUAL(ZeroS, TmpS);
ss.clear();
ss << MaxS;
- BOOST_CHECK(ss.str() == std::string(MaxArray,MaxArray+20));
+ BOOST_CHECK_EQUAL(ss.str(), std::string(MaxArray,MaxArray+20));
ss >> TmpS;
- BOOST_CHECK(MaxS == TmpS);
+ BOOST_CHECK_EQUAL(MaxS, TmpS);
ss.clear();
}
BOOST_AUTO_TEST_CASE( conversion )
{
- BOOST_CHECK(ArithToUint256(UintToArith256(ZeroL)) == ZeroL);
- BOOST_CHECK(ArithToUint256(UintToArith256(OneL)) == OneL);
- BOOST_CHECK(ArithToUint256(UintToArith256(R1L)) == R1L);
- BOOST_CHECK(ArithToUint256(UintToArith256(R2L)) == R2L);
- BOOST_CHECK(UintToArith256(ZeroL) == 0);
- BOOST_CHECK(UintToArith256(OneL) == 1);
- BOOST_CHECK(ArithToUint256(0) == ZeroL);
- BOOST_CHECK(ArithToUint256(1) == OneL);
- BOOST_CHECK(arith_uint256(UintToArith256(uint256S(R1L.GetHex()))) == UintToArith256(R1L));
- BOOST_CHECK(arith_uint256(UintToArith256(uint256S(R2L.GetHex()))) == UintToArith256(R2L));
- BOOST_CHECK(R1L.GetHex() == UintToArith256(R1L).GetHex());
- BOOST_CHECK(R2L.GetHex() == UintToArith256(R2L).GetHex());
+ BOOST_CHECK_EQUAL(ArithToUint256(UintToArith256(ZeroL)), ZeroL);
+ BOOST_CHECK_EQUAL(ArithToUint256(UintToArith256(OneL)), OneL);
+ BOOST_CHECK_EQUAL(ArithToUint256(UintToArith256(R1L)), R1L);
+ BOOST_CHECK_EQUAL(ArithToUint256(UintToArith256(R2L)), R2L);
+ BOOST_CHECK_EQUAL(UintToArith256(ZeroL), 0);
+ BOOST_CHECK_EQUAL(UintToArith256(OneL), 1);
+ BOOST_CHECK_EQUAL(ArithToUint256(0), ZeroL);
+ BOOST_CHECK_EQUAL(ArithToUint256(1), OneL);
+ BOOST_CHECK_EQUAL(arith_uint256(UintToArith256(uint256S(R1L.GetHex()))), UintToArith256(R1L));
+ BOOST_CHECK_EQUAL(arith_uint256(UintToArith256(uint256S(R2L.GetHex()))), UintToArith256(R2L));
+ BOOST_CHECK_EQUAL(R1L.GetHex(), UintToArith256(R1L).GetHex());
+ BOOST_CHECK_EQUAL(R2L.GetHex(), UintToArith256(R2L).GetHex());
}
BOOST_AUTO_TEST_CASE( operator_with_self )
@@ -285,13 +300,13 @@ BOOST_AUTO_TEST_CASE( operator_with_self )
#endif
arith_uint256 v = UintToArith256(uint256S("02"));
v *= v;
- BOOST_CHECK(v == UintToArith256(uint256S("04")));
+ BOOST_CHECK_EQUAL(v, UintToArith256(uint256S("04")));
v /= v;
- BOOST_CHECK(v == UintToArith256(uint256S("01")));
+ BOOST_CHECK_EQUAL(v, UintToArith256(uint256S("01")));
v += v;
- BOOST_CHECK(v == UintToArith256(uint256S("02")));
+ BOOST_CHECK_EQUAL(v, UintToArith256(uint256S("02")));
v -= v;
- BOOST_CHECK(v == UintToArith256(uint256S("0")));
+ BOOST_CHECK_EQUAL(v, UintToArith256(uint256S("0")));
#if defined(__clang__)
# pragma clang diagnostic pop
#endif
@@ -302,7 +317,7 @@ BOOST_AUTO_TEST_CASE(parse)
{
std::string s_12{"0000000000000000000000000000000000000000000000000000000000000012"};
BOOST_CHECK_EQUAL(uint256S("12\0").GetHex(), s_12);
- BOOST_CHECK_EQUAL(uint256S(std::string{"12\0", 3}).GetHex(), s_12);
+ BOOST_CHECK_EQUAL(uint256S(std::string_view{"12\0", 3}).GetHex(), s_12);
BOOST_CHECK_EQUAL(uint256S("0x12").GetHex(), s_12);
BOOST_CHECK_EQUAL(uint256S(" 0x12").GetHex(), s_12);
BOOST_CHECK_EQUAL(uint256S(" 12").GetHex(), s_12);
@@ -310,7 +325,7 @@ BOOST_AUTO_TEST_CASE(parse)
{
std::string s_1{uint256::ONE.GetHex()};
BOOST_CHECK_EQUAL(uint256S("1\0").GetHex(), s_1);
- BOOST_CHECK_EQUAL(uint256S(std::string{"1\0", 2}).GetHex(), s_1);
+ BOOST_CHECK_EQUAL(uint256S(std::string_view{"1\0", 2}).GetHex(), s_1);
BOOST_CHECK_EQUAL(uint256S("0x1").GetHex(), s_1);
BOOST_CHECK_EQUAL(uint256S(" 0x1").GetHex(), s_1);
BOOST_CHECK_EQUAL(uint256S(" 1").GetHex(), s_1);
@@ -318,17 +333,77 @@ BOOST_AUTO_TEST_CASE(parse)
{
std::string s_0{uint256::ZERO.GetHex()};
BOOST_CHECK_EQUAL(uint256S("\0").GetHex(), s_0);
- BOOST_CHECK_EQUAL(uint256S(std::string{"\0", 1}).GetHex(), s_0);
+ BOOST_CHECK_EQUAL(uint256S(std::string_view{"\0", 1}).GetHex(), s_0);
BOOST_CHECK_EQUAL(uint256S("0x").GetHex(), s_0);
BOOST_CHECK_EQUAL(uint256S(" 0x").GetHex(), s_0);
BOOST_CHECK_EQUAL(uint256S(" ").GetHex(), s_0);
}
}
+/**
+ * Implemented as a templated function so it can be reused by other classes that have a FromHex()
+ * method that wraps base_blob::FromHex(), such as transaction_identifier::FromHex().
+ */
+template <typename T>
+void TestFromHex()
+{
+ constexpr unsigned int num_chars{T::size() * 2};
+ static_assert(num_chars <= 64); // this test needs to be modified to allow for more than 64 hex chars
+ const std::string valid_64char_input{"0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDEF"};
+ const auto valid_input{valid_64char_input.substr(0, num_chars)};
+ {
+ // check that lower and upper case hex characters are accepted
+ auto valid_result{T::FromHex(valid_input)};
+ BOOST_REQUIRE(valid_result);
+ BOOST_CHECK_EQUAL(valid_result->ToString(), ToLower(valid_input));
+ }
+ {
+ // check that only strings of size num_chars are accepted
+ BOOST_CHECK(!T::FromHex(""));
+ BOOST_CHECK(!T::FromHex("0"));
+ BOOST_CHECK(!T::FromHex(valid_input.substr(0, num_chars / 2)));
+ BOOST_CHECK(!T::FromHex(valid_input.substr(0, num_chars - 1)));
+ BOOST_CHECK(!T::FromHex(valid_input + "0"));
+ }
+ {
+ // check that non-hex characters are not accepted
+ std::string invalid_chars{R"( !"#$%&'()*+,-./:;<=>?@GHIJKLMNOPQRSTUVWXYZ[\]^_`ghijklmnopqrstuvwxyz{|}~)"};
+ for (auto c : invalid_chars) {
+ BOOST_CHECK(!T::FromHex(valid_input.substr(0, num_chars - 1) + c));
+ }
+ // 0x prefixes are invalid
+ std::string invalid_prefix{"0x" + valid_input};
+ BOOST_CHECK(!T::FromHex(std::string_view(invalid_prefix.data(), num_chars)));
+ BOOST_CHECK(!T::FromHex(invalid_prefix));
+ }
+ {
+ // check that string_view length is respected
+ std::string chars_68{valid_64char_input + "0123"};
+ BOOST_CHECK_EQUAL(T::FromHex(std::string_view(chars_68.data(), num_chars)).value().ToString(), ToLower(valid_input));
+ BOOST_CHECK(!T::FromHex(std::string_view(chars_68.data(), num_chars - 1))); // too short
+ BOOST_CHECK(!T::FromHex(std::string_view(chars_68.data(), num_chars + 1))); // too long
+ }
+}
+
+BOOST_AUTO_TEST_CASE(from_hex)
+{
+ TestFromHex<uint160>();
+ TestFromHex<uint256>();
+ TestFromHex<Txid>();
+ TestFromHex<Wtxid>();
+}
+
BOOST_AUTO_TEST_CASE( check_ONE )
{
- uint256 one = uint256S("0000000000000000000000000000000000000000000000000000000000000001");
+ uint256 one = uint256{"0000000000000000000000000000000000000000000000000000000000000001"};
BOOST_CHECK_EQUAL(one, uint256::ONE);
}
+BOOST_AUTO_TEST_CASE(FromHex_vs_uint256)
+{
+ auto runtime_uint{uint256::FromHex("4A5E1E4BAAB89F3A32518A88C31BC87F618f76673e2cc77ab2127b7afdeda33b").value()};
+ constexpr uint256 consteval_uint{ "4a5e1e4baab89f3a32518a88c31bc87f618F76673E2CC77AB2127B7AFDEDA33B"};
+ BOOST_CHECK_EQUAL(consteval_uint, runtime_uint);
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/util/chainstate.h b/src/test/util/chainstate.h
index a4636365ca..e604b44c16 100644
--- a/src/test/util/chainstate.h
+++ b/src/test/util/chainstate.h
@@ -99,7 +99,7 @@ CreateAndActivateUTXOSnapshot(
assert(pindex->IsValid(BlockStatus::BLOCK_VALID_TREE));
pindex->nStatus = BlockStatus::BLOCK_VALID_TREE;
pindex->nTx = 0;
- pindex->nChainTx = 0;
+ pindex->m_chain_tx_count = 0;
pindex->nSequenceId = 0;
pindex = pindex->pprev;
}
diff --git a/src/test/util/cluster_linearize.h b/src/test/util/cluster_linearize.h
new file mode 100644
index 0000000000..508a08133c
--- /dev/null
+++ b/src/test/util/cluster_linearize.h
@@ -0,0 +1,353 @@
+// Copyright (c) The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_TEST_UTIL_CLUSTER_LINEARIZE_H
+#define BITCOIN_TEST_UTIL_CLUSTER_LINEARIZE_H
+
+#include <cluster_linearize.h>
+#include <serialize.h>
+#include <span.h>
+#include <streams.h>
+#include <util/bitset.h>
+#include <util/feefrac.h>
+
+#include <stdint.h>
+#include <numeric>
+#include <vector>
+#include <utility>
+
+namespace {
+
+using namespace cluster_linearize;
+
+using TestBitSet = BitSet<32>;
+
+/** Check if a graph is acyclic. */
+template<typename SetType>
+bool IsAcyclic(const DepGraph<SetType>& depgraph) noexcept
+{
+ for (ClusterIndex i = 0; i < depgraph.TxCount(); ++i) {
+ if ((depgraph.Ancestors(i) & depgraph.Descendants(i)) != SetType::Singleton(i)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+/** A formatter for a bespoke serialization for acyclic DepGraph objects.
+ *
+ * The serialization format outputs information about transactions in a topological order (parents
+ * before children), together with position information so transactions can be moved back to their
+ * correct position on deserialization.
+ *
+ * - For each transaction t in the DepGraph (in some topological order);
+ * - The size: VARINT(t.size), which cannot be 0.
+ * - The fee: VARINT(SignedToUnsigned(t.fee)), see below for SignedToUnsigned.
+ * - For each direct dependency:
+ * - VARINT(skip)
+ * - The position of t in the cluster: VARINT(skip)
+ * - The end of the graph: VARINT(0)
+ *
+ * The list of skip values encodes the dependencies of t, as well as its position in the cluster.
+ * Each skip value is the number of possibilities that were available, but were not taken. These
+ * possibilities are, in order:
+ * - For each previous transaction in the graph, in reverse serialization order, whether it is a
+ * direct parent of t (but excluding transactions which are already implied to be dependencies
+ * by parent relations that were serialized before it).
+ * - The various insertion positions in the cluster, from the very end of the cluster, to the
+ * front.
+ *
+ * Let's say you have a 7-transaction cluster, consisting of transactions F,A,C,B,G,E,D, but
+ * serialized in order A,B,C,D,E,F,G, because that happens to be a topological ordering. By the
+ * time G gets serialized, what has been serialized already represents the cluster F,A,C,B,E,D (in
+ * that order). G has B and E as direct parents, and E depends on C.
+ *
+ * In this case, the possibilities are, in order:
+ * - [ ] the dependency G->F
+ * - [X] the dependency G->E
+ * - [ ] the dependency G->D
+ * - [X] the dependency G->B
+ * - [ ] the dependency G->A
+ * - [ ] put G at the end of the cluster
+ * - [ ] put G before D
+ * - [X] put G before E
+ * - [ ] put G before B
+ * - [ ] put G before C
+ * - [ ] put G before A
+ * - [ ] put G before F
+ *
+ * The skip values in this case are 1 (G->F), 1 (G->D), 3 (G->A, G at end, G before D). No skip
+ * after 3 is needed (or permitted), because there can only be one position for G. Also note that
+ * G->C is not included in the list of possibilities, as it is implied by the included G->E and
+ * E->C that came before it. On deserialization, if the last skip value was 8 or larger (putting
+ * G before the beginning of the cluster), it is interpreted as wrapping around back to the end.
+ *
+ *
+ * Rationale:
+ * - Why VARINTs? They are flexible enough to represent large numbers where needed, but more
+ * compact for smaller numbers. The serialization format is designed so that simple structures
+ * involve smaller numbers, so smaller size maps to simpler graphs.
+ * - Why use SignedToUnsigned? It results in small unsigned values for signed values with small
+ * absolute value. This way we can encode negative fees in graphs, but still let small negative
+ * numbers have small encodings.
+ * - Why are the parents emitted in reverse order compared to the transactions themselves? This
+ * naturally lets us skip parents-of-parents, as they will be reflected as implied dependencies.
+ * - Why encode skip values and not a bitmask to convey the list positions? It turns out that the
+ * most complex graphs (in terms of linearization complexity) are ones with ~1 dependency per
+ * transaction. The current encoding uses ~1 byte per transaction for dependencies in this case,
+ * while a bitmask would require ~N/2 bits per transaction.
+ */
+
+struct DepGraphFormatter
+{
+ /** Convert x>=0 to 2x (even), x<0 to -2x-1 (odd). */
+ static uint64_t SignedToUnsigned(int64_t x) noexcept
+ {
+ if (x < 0) {
+ return 2 * uint64_t(-(x + 1)) + 1;
+ } else {
+ return 2 * uint64_t(x);
+ }
+ }
+
+ /** Convert even x to x/2 (>=0), odd x to -(x/2)-1 (<0). */
+ static int64_t UnsignedToSigned(uint64_t x) noexcept
+ {
+ if (x & 1) {
+ return -int64_t(x / 2) - 1;
+ } else {
+ return int64_t(x / 2);
+ }
+ }
+
+ template <typename Stream, typename SetType>
+ static void Ser(Stream& s, const DepGraph<SetType>& depgraph)
+ {
+ /** Construct a topological order to serialize the transactions in. */
+ std::vector<ClusterIndex> topo_order(depgraph.TxCount());
+ std::iota(topo_order.begin(), topo_order.end(), ClusterIndex{0});
+ std::sort(topo_order.begin(), topo_order.end(), [&](ClusterIndex a, ClusterIndex b) {
+ auto anc_a = depgraph.Ancestors(a).Count(), anc_b = depgraph.Ancestors(b).Count();
+ if (anc_a != anc_b) return anc_a < anc_b;
+ return a < b;
+ });
+
+ /** Which transactions the deserializer already knows when it has deserialized what has
+ * been serialized here so far, and in what order. */
+ std::vector<ClusterIndex> rebuilt_order;
+ rebuilt_order.reserve(depgraph.TxCount());
+
+ // Loop over the transactions in topological order.
+ for (ClusterIndex topo_idx = 0; topo_idx < topo_order.size(); ++topo_idx) {
+ /** Which depgraph index we are currently writing. */
+ ClusterIndex idx = topo_order[topo_idx];
+ // Write size, which must be larger than 0.
+ s << VARINT_MODE(depgraph.FeeRate(idx).size, VarIntMode::NONNEGATIVE_SIGNED);
+ // Write fee, encoded as an unsigned varint (odd=negative, even=non-negative).
+ s << VARINT(SignedToUnsigned(depgraph.FeeRate(idx).fee));
+ // Write dependency information.
+ SetType written_parents;
+ uint64_t diff = 0; //!< How many potential parent/child relations we have skipped over.
+ for (ClusterIndex dep_dist = 0; dep_dist < topo_idx; ++dep_dist) {
+ /** Which depgraph index we are currently considering as parent of idx. */
+ ClusterIndex dep_idx = topo_order[topo_idx - 1 - dep_dist];
+ // Ignore transactions which are already known to be ancestors.
+ if (depgraph.Descendants(dep_idx).Overlaps(written_parents)) continue;
+ if (depgraph.Ancestors(idx)[dep_idx]) {
+ // When an actual parent is encounted, encode how many non-parents were skipped
+ // before it.
+ s << VARINT(diff);
+ diff = 0;
+ written_parents.Set(dep_idx);
+ } else {
+ // When a non-parent is encountered, increment the skip counter.
+ ++diff;
+ }
+ }
+ // Write position information.
+ ClusterIndex insert_distance = 0;
+ while (insert_distance < rebuilt_order.size()) {
+ // Loop to find how far from the end in rebuilt_order to insert.
+ if (idx > *(rebuilt_order.end() - 1 - insert_distance)) break;
+ ++insert_distance;
+ }
+ rebuilt_order.insert(rebuilt_order.end() - insert_distance, idx);
+ s << VARINT(diff + insert_distance);
+ }
+
+ // Output a final 0 to denote the end of the graph.
+ s << uint8_t{0};
+ }
+
+ template <typename Stream, typename SetType>
+ void Unser(Stream& s, DepGraph<SetType>& depgraph)
+ {
+ /** The dependency graph which we deserialize into first, with transactions in
+ * topological serialization order, not original cluster order. */
+ DepGraph<SetType> topo_depgraph;
+ /** Mapping from cluster order to serialization order, used later to reconstruct the
+ * cluster order. */
+ std::vector<ClusterIndex> reordering;
+
+ // Read transactions in topological order.
+ try {
+ while (true) {
+ // Read size. Size 0 signifies the end of the DepGraph.
+ int32_t size;
+ s >> VARINT_MODE(size, VarIntMode::NONNEGATIVE_SIGNED);
+ size &= 0x3FFFFF; // Enough for size up to 4M.
+ static_assert(0x3FFFFF >= 4000000);
+ if (size == 0 || topo_depgraph.TxCount() == SetType::Size()) break;
+ // Read fee, encoded as an unsigned varint (odd=negative, even=non-negative).
+ uint64_t coded_fee;
+ s >> VARINT(coded_fee);
+ coded_fee &= 0xFFFFFFFFFFFFF; // Enough for fee between -21M...21M BTC.
+ static_assert(0xFFFFFFFFFFFFF > uint64_t{2} * 21000000 * 100000000);
+ auto fee = UnsignedToSigned(coded_fee);
+ // Extend topo_depgraph with the new transaction (at the end).
+ auto topo_idx = topo_depgraph.AddTransaction({fee, size});
+ reordering.push_back(topo_idx);
+ // Read dependency information.
+ uint64_t diff = 0; //!< How many potential parents we have to skip.
+ s >> VARINT(diff);
+ for (ClusterIndex dep_dist = 0; dep_dist < topo_idx; ++dep_dist) {
+ /** Which topo_depgraph index we are currently considering as parent of topo_idx. */
+ ClusterIndex dep_topo_idx = topo_idx - 1 - dep_dist;
+ // Ignore transactions which are already known ancestors of topo_idx.
+ if (topo_depgraph.Descendants(dep_topo_idx)[topo_idx]) continue;
+ if (diff == 0) {
+ // When the skip counter has reached 0, add an actual dependency.
+ topo_depgraph.AddDependency(dep_topo_idx, topo_idx);
+ // And read the number of skips after it.
+ s >> VARINT(diff);
+ } else {
+ // Otherwise, dep_topo_idx is not a parent. Decrement and continue.
+ --diff;
+ }
+ }
+ // If we reach this point, we can interpret the remaining skip value as how far from the
+ // end of reordering topo_idx should be placed (wrapping around), so move it to its
+ // correct location. The preliminary reordering.push_back(topo_idx) above was to make
+ // sure that if a deserialization exception occurs, topo_idx still appears somewhere.
+ reordering.pop_back();
+ reordering.insert(reordering.end() - (diff % (reordering.size() + 1)), topo_idx);
+ }
+ } catch (const std::ios_base::failure&) {}
+
+ // Construct the original cluster order depgraph.
+ depgraph = {};
+ // Add transactions to depgraph in the original cluster order.
+ for (auto topo_idx : reordering) {
+ depgraph.AddTransaction(topo_depgraph.FeeRate(topo_idx));
+ }
+ // Translate dependencies from topological to cluster order.
+ for (ClusterIndex idx = 0; idx < reordering.size(); ++idx) {
+ ClusterIndex topo_idx = reordering[idx];
+ for (ClusterIndex dep_idx = 0; dep_idx < reordering.size(); ++dep_idx) {
+ ClusterIndex dep_topo_idx = reordering[dep_idx];
+ if (topo_depgraph.Ancestors(topo_idx)[dep_topo_idx]) {
+ depgraph.AddDependency(dep_idx, idx);
+ }
+ }
+ }
+ }
+};
+
+/** Perform a sanity/consistency check on a DepGraph. */
+template<typename SetType>
+void SanityCheck(const DepGraph<SetType>& depgraph)
+{
+ // Consistency check between ancestors internally.
+ for (ClusterIndex i = 0; i < depgraph.TxCount(); ++i) {
+ // Transactions include themselves as ancestors.
+ assert(depgraph.Ancestors(i)[i]);
+ // If a is an ancestor of b, then b's ancestors must include all of a's ancestors.
+ for (auto a : depgraph.Ancestors(i)) {
+ assert(depgraph.Ancestors(i).IsSupersetOf(depgraph.Ancestors(a)));
+ }
+ }
+ // Consistency check between ancestors and descendants.
+ for (ClusterIndex i = 0; i < depgraph.TxCount(); ++i) {
+ for (ClusterIndex j = 0; j < depgraph.TxCount(); ++j) {
+ assert(depgraph.Ancestors(i)[j] == depgraph.Descendants(j)[i]);
+ }
+ }
+ // If DepGraph is acyclic, serialize + deserialize must roundtrip.
+ if (IsAcyclic(depgraph)) {
+ std::vector<unsigned char> ser;
+ VectorWriter writer(ser, 0);
+ writer << Using<DepGraphFormatter>(depgraph);
+ SpanReader reader(ser);
+ DepGraph<TestBitSet> decoded_depgraph;
+ reader >> Using<DepGraphFormatter>(decoded_depgraph);
+ assert(depgraph == decoded_depgraph);
+ assert(reader.empty());
+ // It must also deserialize correctly without the terminal 0 byte (as the deserializer
+ // will upon EOF still return what it read so far).
+ assert(ser.size() >= 1 && ser.back() == 0);
+ ser.pop_back();
+ reader = SpanReader{ser};
+ decoded_depgraph = {};
+ reader >> Using<DepGraphFormatter>(decoded_depgraph);
+ assert(depgraph == decoded_depgraph);
+ assert(reader.empty());
+ }
+}
+
+/** Verify that a DepGraph corresponds to the information in a cluster. */
+template<typename SetType>
+void VerifyDepGraphFromCluster(const Cluster<SetType>& cluster, const DepGraph<SetType>& depgraph)
+{
+ // Sanity check the depgraph, which includes a check for correspondence between ancestors and
+ // descendants, so it suffices to check just ancestors below.
+ SanityCheck(depgraph);
+ // Verify transaction count.
+ assert(cluster.size() == depgraph.TxCount());
+ // Verify feerates.
+ for (ClusterIndex i = 0; i < depgraph.TxCount(); ++i) {
+ assert(depgraph.FeeRate(i) == cluster[i].first);
+ }
+ // Verify ancestors.
+ for (ClusterIndex i = 0; i < depgraph.TxCount(); ++i) {
+ // Start with the transaction having itself as ancestor.
+ auto ancestors = SetType::Singleton(i);
+ // Add parents of ancestors to the set of ancestors until it stops changing.
+ while (true) {
+ const auto old_ancestors = ancestors;
+ for (auto ancestor : ancestors) {
+ ancestors |= cluster[ancestor].second;
+ }
+ if (old_ancestors == ancestors) break;
+ }
+ // Compare against depgraph.
+ assert(depgraph.Ancestors(i) == ancestors);
+ // Some additional sanity tests:
+ // - Every transaction has itself as ancestor.
+ assert(ancestors[i]);
+ // - Every transaction has its direct parents as ancestors.
+ for (auto parent : cluster[i].second) {
+ assert(ancestors[parent]);
+ }
+ }
+}
+
+/** Perform a sanity check on a linearization. */
+template<typename SetType>
+void SanityCheck(const DepGraph<SetType>& depgraph, Span<const ClusterIndex> linearization)
+{
+ // Check completeness.
+ assert(linearization.size() == depgraph.TxCount());
+ TestBitSet done;
+ for (auto i : linearization) {
+ // Check transaction position is in range.
+ assert(i < depgraph.TxCount());
+ // Check topology and lack of duplicates.
+ assert((depgraph.Ancestors(i) - done) == TestBitSet::Singleton(i));
+ done.Set(i);
+ }
+}
+
+} // namespace
+
+#endif // BITCOIN_TEST_UTIL_CLUSTER_LINEARIZE_H
diff --git a/src/test/util/net.cpp b/src/test/util/net.cpp
index 9257a4964a..beefc32bee 100644
--- a/src/test/util/net.cpp
+++ b/src/test/util/net.cpp
@@ -28,7 +28,8 @@ void ConnmanTestMsg::Handshake(CNode& node,
auto& connman{*this};
peerman.InitializeNode(node, local_services);
- FlushSendBuffer(node); // Drop the version message added by InitializeNode.
+ peerman.SendMessages(&node);
+ FlushSendBuffer(node); // Drop the version message added by SendMessages.
CSerializedNetMsg msg_version{
NetMsg::Make(NetMsgType::VERSION,
@@ -118,20 +119,20 @@ std::vector<NodeEvictionCandidate> GetRandomNodeEvictionCandidates(int n_candida
candidates.reserve(n_candidates);
for (int id = 0; id < n_candidates; ++id) {
candidates.push_back({
- /*id=*/id,
- /*m_connected=*/std::chrono::seconds{random_context.randrange(100)},
- /*m_min_ping_time=*/std::chrono::microseconds{random_context.randrange(100)},
- /*m_last_block_time=*/std::chrono::seconds{random_context.randrange(100)},
- /*m_last_tx_time=*/std::chrono::seconds{random_context.randrange(100)},
- /*fRelevantServices=*/random_context.randbool(),
- /*m_relay_txs=*/random_context.randbool(),
- /*fBloomFilter=*/random_context.randbool(),
- /*nKeyedNetGroup=*/random_context.randrange(100),
- /*prefer_evict=*/random_context.randbool(),
- /*m_is_local=*/random_context.randbool(),
- /*m_network=*/ALL_NETWORKS[random_context.randrange(ALL_NETWORKS.size())],
- /*m_noban=*/false,
- /*m_conn_type=*/ConnectionType::INBOUND,
+ .id=id,
+ .m_connected=std::chrono::seconds{random_context.randrange(100)},
+ .m_min_ping_time=std::chrono::microseconds{random_context.randrange(100)},
+ .m_last_block_time=std::chrono::seconds{random_context.randrange(100)},
+ .m_last_tx_time=std::chrono::seconds{random_context.randrange(100)},
+ .fRelevantServices=random_context.randbool(),
+ .m_relay_txs=random_context.randbool(),
+ .fBloomFilter=random_context.randbool(),
+ .nKeyedNetGroup=random_context.randrange(100u),
+ .prefer_evict=random_context.randbool(),
+ .m_is_local=random_context.randbool(),
+ .m_network=ALL_NETWORKS[random_context.randrange(ALL_NETWORKS.size())],
+ .m_noban=false,
+ .m_conn_type=ConnectionType::INBOUND,
});
}
return candidates;
diff --git a/src/test/util/net.h b/src/test/util/net.h
index d91d801132..043e317bf0 100644
--- a/src/test/util/net.h
+++ b/src/test/util/net.h
@@ -34,6 +34,11 @@ class Span;
struct ConnmanTestMsg : public CConnman {
using CConnman::CConnman;
+ void SetMsgProc(NetEventsInterface* msgproc)
+ {
+ m_msgproc = msgproc;
+ }
+
void SetPeerConnectTimeout(std::chrono::seconds timeout)
{
m_peer_connect_timeout = timeout;
diff --git a/src/test/util/random.cpp b/src/test/util/random.cpp
index 4c87ab8df8..47d03055e2 100644
--- a/src/test/util/random.cpp
+++ b/src/test/util/random.cpp
@@ -13,21 +13,26 @@
FastRandomContext g_insecure_rand_ctx;
-/** Return the unsigned from the environment var if available, otherwise 0 */
-static uint256 GetUintFromEnv(const std::string& env_name)
-{
- const char* num = std::getenv(env_name.c_str());
- if (!num) return {};
- return uint256S(num);
-}
+extern void MakeRandDeterministicDANGEROUS(const uint256& seed) noexcept;
-void Seed(FastRandomContext& ctx)
+void SeedRandomForTest(SeedRand seedtype)
{
- // Should be enough to get the seed once for the process
- static uint256 seed{};
static const std::string RANDOM_CTX_SEED{"RANDOM_CTX_SEED"};
- if (seed.IsNull()) seed = GetUintFromEnv(RANDOM_CTX_SEED);
- if (seed.IsNull()) seed = GetRandHash();
+
+ // Do this once, on the first call, regardless of seedtype, because once
+ // MakeRandDeterministicDANGEROUS is called, the output of GetRandHash is
+ // no longer truly random. It should be enough to get the seed once for the
+ // process.
+ static const uint256 ctx_seed = []() {
+ // If RANDOM_CTX_SEED is set, use that as seed.
+ const char* num = std::getenv(RANDOM_CTX_SEED.c_str());
+ if (num) return uint256S(num);
+ // Otherwise use a (truly) random value.
+ return GetRandHash();
+ }();
+
+ const uint256& seed{seedtype == SeedRand::SEED ? ctx_seed : uint256::ZERO};
LogPrintf("%s: Setting random seed for current tests to %s=%s\n", __func__, RANDOM_CTX_SEED, seed.GetHex());
- ctx = FastRandomContext(seed);
+ MakeRandDeterministicDANGEROUS(seed);
+ g_insecure_rand_ctx.Reseed(GetRandHash());
}
diff --git a/src/test/util/random.h b/src/test/util/random.h
index 18ab425e48..09a475f8b3 100644
--- a/src/test/util/random.h
+++ b/src/test/util/random.h
@@ -19,27 +19,13 @@
*/
extern FastRandomContext g_insecure_rand_ctx;
-/**
- * Flag to make GetRand in random.h return the same number
- */
-extern bool g_mock_deterministic_tests;
-
enum class SeedRand {
ZEROS, //!< Seed with a compile time constant of zeros
- SEED, //!< Call the Seed() helper
+ SEED, //!< Use (and report) random seed from environment, or a (truly) random one.
};
-/** Seed the given random ctx or use the seed passed in via an environment var */
-void Seed(FastRandomContext& ctx);
-
-static inline void SeedInsecureRand(SeedRand seed = SeedRand::SEED)
-{
- if (seed == SeedRand::ZEROS) {
- g_insecure_rand_ctx = FastRandomContext(/*fDeterministic=*/true);
- } else {
- Seed(g_insecure_rand_ctx);
- }
-}
+/** Seed the RNG for testing. This affects all randomness, except GetStrongRandBytes(). */
+void SeedRandomForTest(SeedRand seed = SeedRand::SEED);
static inline uint32_t InsecureRand32()
{
diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp
index cc7b2d6546..62ff61b227 100644
--- a/src/test/util/setup_common.cpp
+++ b/src/test/util/setup_common.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2011-2022 The Bitcoin Core developers
+// Copyright (c) 2011-present The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -6,8 +6,6 @@
#include <test/util/setup_common.h>
-#include <kernel/validation_cache_sizes.h>
-
#include <addrman.h>
#include <banman.h>
#include <chainparams.h>
@@ -30,11 +28,9 @@
#include <node/mempool_args.h>
#include <node/miner.h>
#include <node/peerman_args.h>
-#include <node/validation_cache_args.h>
#include <node/warnings.h>
#include <noui.h>
#include <policy/fees.h>
-#include <policy/fees_args.h>
#include <pow.h>
#include <random.h>
#include <rpc/blockchain.h>
@@ -68,7 +64,6 @@
#include <stdexcept>
using kernel::BlockTreeDB;
-using kernel::ValidationCacheSizes;
using node::ApplyArgsManOptions;
using node::BlockAssembler;
using node::BlockManager;
@@ -83,6 +78,18 @@ const std::function<std::string(const char*)> G_TRANSLATION_FUN = nullptr;
/** Random context to get unique temp data dirs. Separate from g_insecure_rand_ctx, which can be seeded from a const env var */
static FastRandomContext g_insecure_rand_ctx_temp_path;
+std::ostream& operator<<(std::ostream& os, const arith_uint256& num)
+{
+ os << num.ToString();
+ return os;
+}
+
+std::ostream& operator<<(std::ostream& os, const uint160& num)
+{
+ os << num.ToString();
+ return os;
+}
+
std::ostream& operator<<(std::ostream& os, const uint256& num)
{
os << num.ToString();
@@ -112,7 +119,7 @@ static void ExitFailure(std::string_view str_err)
exit(EXIT_FAILURE);
}
-BasicTestingSetup::BasicTestingSetup(const ChainType chainType, const std::vector<const char*>& extra_args)
+BasicTestingSetup::BasicTestingSetup(const ChainType chainType, TestOpts opts)
: m_args{}
{
m_node.shutdown = &m_interrupt;
@@ -129,7 +136,7 @@ BasicTestingSetup::BasicTestingSetup(const ChainType chainType, const std::vecto
"-debugexclude=libevent",
"-debugexclude=leveldb",
},
- extra_args);
+ opts.extra_args);
if (G_TEST_COMMAND_LINE_ARGUMENTS) {
arguments = Cat(arguments, G_TEST_COMMAND_LINE_ARGUMENTS());
}
@@ -145,6 +152,10 @@ BasicTestingSetup::BasicTestingSetup(const ChainType chainType, const std::vecto
}
}
+ // Use randomly chosen seed for deterministic PRNG, so that (by default) test
+ // data directories use a random name that doesn't overlap with other tests.
+ SeedRandomForTest(SeedRand::SEED);
+
if (!m_node.args->IsArgSet("-testdatadir")) {
// By default, the data directory has a random name
const auto rand_str{g_insecure_rand_ctx_temp_path.rand256().ToString()};
@@ -178,7 +189,6 @@ BasicTestingSetup::BasicTestingSetup(const ChainType chainType, const std::vecto
gArgs.ForceSetArg("-datadir", fs::PathToString(m_path_root));
SelectParams(chainType);
- SeedInsecureRand();
if (G_TEST_LOG_FUN) LogInstance().PushBackCallback(G_TEST_LOG_FUN);
InitLogging(*m_node.args);
AppInitParameterInteraction(*m_node.args);
@@ -188,11 +198,6 @@ BasicTestingSetup::BasicTestingSetup(const ChainType chainType, const std::vecto
m_node.ecc_context = std::make_unique<ECC_Context>();
SetupEnvironment();
- ValidationCacheSizes validation_cache_sizes{};
- ApplyArgsManOptions(*m_node.args, validation_cache_sizes);
- Assert(InitSignatureCache(validation_cache_sizes.signature_cache_bytes));
- Assert(InitScriptExecutionCache(validation_cache_sizes.script_execution_cache_bytes));
-
m_node.chain = interfaces::MakeChain(m_node);
static bool noui_connected = false;
if (!noui_connected) {
@@ -217,18 +222,19 @@ BasicTestingSetup::~BasicTestingSetup()
gArgs.ClearArgs();
}
-ChainTestingSetup::ChainTestingSetup(const ChainType chainType, const std::vector<const char*>& extra_args)
- : BasicTestingSetup(chainType, extra_args)
+ChainTestingSetup::ChainTestingSetup(const ChainType chainType, TestOpts opts)
+ : BasicTestingSetup(chainType, opts)
{
const CChainParams& chainparams = Params();
// We have to run a scheduler thread to prevent ActivateBestChain
// from blocking due to queue overrun.
- m_node.scheduler = std::make_unique<CScheduler>();
- m_node.scheduler->m_service_thread = std::thread(util::TraceThread, "scheduler", [&] { m_node.scheduler->serviceQueue(); });
- m_node.validation_signals = std::make_unique<ValidationSignals>(std::make_unique<SerialTaskRunner>(*m_node.scheduler));
+ if (opts.setup_validation_interface) {
+ m_node.scheduler = std::make_unique<CScheduler>();
+ m_node.scheduler->m_service_thread = std::thread(util::TraceThread, "scheduler", [&] { m_node.scheduler->serviceQueue(); });
+ m_node.validation_signals = std::make_unique<ValidationSignals>(std::make_unique<SerialTaskRunner>(*m_node.scheduler));
+ }
- m_node.fee_estimator = std::make_unique<CBlockPolicyEstimator>(FeeestPath(*m_node.args), DEFAULT_ACCEPT_STALE_FEE_ESTIMATES);
bilingual_str error{};
m_node.mempool = std::make_unique<CTxMemPool>(MemPoolOptionsForTest(m_node), error);
Assert(error.empty());
@@ -238,37 +244,47 @@ ChainTestingSetup::ChainTestingSetup(const ChainType chainType, const std::vecto
m_node.notifications = std::make_unique<KernelNotifications>(*Assert(m_node.shutdown), m_node.exit_status, *Assert(m_node.warnings));
- const ChainstateManager::Options chainman_opts{
- .chainparams = chainparams,
- .datadir = m_args.GetDataDirNet(),
- .check_block_index = 1,
- .notifications = *m_node.notifications,
- .signals = m_node.validation_signals.get(),
- .worker_threads_num = 2,
- };
- const BlockManager::Options blockman_opts{
- .chainparams = chainman_opts.chainparams,
- .blocks_dir = m_args.GetBlocksDirPath(),
- .notifications = chainman_opts.notifications,
+ m_make_chainman = [this, &chainparams, opts] {
+ Assert(!m_node.chainman);
+ ChainstateManager::Options chainman_opts{
+ .chainparams = chainparams,
+ .datadir = m_args.GetDataDirNet(),
+ .check_block_index = 1,
+ .notifications = *m_node.notifications,
+ .signals = m_node.validation_signals.get(),
+ .worker_threads_num = 2,
+ };
+ if (opts.min_validation_cache) {
+ chainman_opts.script_execution_cache_bytes = 0;
+ chainman_opts.signature_cache_bytes = 0;
+ }
+ const BlockManager::Options blockman_opts{
+ .chainparams = chainman_opts.chainparams,
+ .blocks_dir = m_args.GetBlocksDirPath(),
+ .notifications = chainman_opts.notifications,
+ };
+ m_node.chainman = std::make_unique<ChainstateManager>(*Assert(m_node.shutdown), chainman_opts, blockman_opts);
+ LOCK(m_node.chainman->GetMutex());
+ m_node.chainman->m_blockman.m_block_tree_db = std::make_unique<BlockTreeDB>(DBParams{
+ .path = m_args.GetDataDirNet() / "blocks" / "index",
+ .cache_bytes = static_cast<size_t>(m_cache_sizes.block_tree_db),
+ .memory_only = true,
+ });
};
- m_node.chainman = std::make_unique<ChainstateManager>(*Assert(m_node.shutdown), chainman_opts, blockman_opts);
- m_node.chainman->m_blockman.m_block_tree_db = std::make_unique<BlockTreeDB>(DBParams{
- .path = m_args.GetDataDirNet() / "blocks" / "index",
- .cache_bytes = static_cast<size_t>(m_cache_sizes.block_tree_db),
- .memory_only = true});
+ m_make_chainman();
}
ChainTestingSetup::~ChainTestingSetup()
{
if (m_node.scheduler) m_node.scheduler->stop();
- m_node.validation_signals->FlushBackgroundCallbacks();
+ if (m_node.validation_signals) m_node.validation_signals->FlushBackgroundCallbacks();
m_node.connman.reset();
m_node.banman.reset();
m_node.addrman.reset();
m_node.netgroupman.reset();
m_node.args = nullptr;
m_node.mempool.reset();
- m_node.fee_estimator.reset();
+ Assert(!m_node.fee_estimator); // Each test must create a local object, if they wish to use the fee_estimator
m_node.chainman.reset();
m_node.validation_signals.reset();
m_node.scheduler.reset();
@@ -301,19 +317,19 @@ void ChainTestingSetup::LoadVerifyActivateChainstate()
TestingSetup::TestingSetup(
const ChainType chainType,
- const std::vector<const char*>& extra_args,
- const bool coins_db_in_memory,
- const bool block_tree_db_in_memory)
- : ChainTestingSetup(chainType, extra_args)
+ TestOpts opts)
+ : ChainTestingSetup(chainType, opts)
{
- m_coins_db_in_memory = coins_db_in_memory;
- m_block_tree_db_in_memory = block_tree_db_in_memory;
+ m_coins_db_in_memory = opts.coins_db_in_memory;
+ m_block_tree_db_in_memory = opts.block_tree_db_in_memory;
// Ideally we'd move all the RPC tests to the functional testing framework
// instead of unit tests, but for now we need these here.
RegisterAllCoreRPCCommands(tableRPC);
LoadVerifyActivateChainstate();
+ if (!opts.setup_net) return;
+
m_node.netgroupman = std::make_unique<NetGroupManager>(/*asmap=*/std::vector<bool>());
m_node.addrman = std::make_unique<AddrMan>(*m_node.netgroupman,
/*deterministic=*/false,
@@ -336,11 +352,9 @@ TestingSetup::TestingSetup(
}
TestChain100Setup::TestChain100Setup(
- const ChainType chain_type,
- const std::vector<const char*>& extra_args,
- const bool coins_db_in_memory,
- const bool block_tree_db_in_memory)
- : TestingSetup{ChainType::REGTEST, extra_args, coins_db_in_memory, block_tree_db_in_memory}
+ const ChainType chain_type,
+ TestOpts opts)
+ : TestingSetup{ChainType::REGTEST, opts}
{
SetMockTime(1598887952);
constexpr std::array<unsigned char, 32> vchKey = {
diff --git a/src/test/util/setup_common.h b/src/test/util/setup_common.h
index dbd66e3585..b73acc1de5 100644
--- a/src/test/util/setup_common.h
+++ b/src/test/util/setup_common.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2015-2022 The Bitcoin Core developers
+// Copyright (c) 2015-present The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -24,6 +24,7 @@
#include <type_traits>
#include <vector>
+class arith_uint256;
class CFeeRate;
class Chainstate;
class FastRandomContext;
@@ -48,6 +49,15 @@ std::ostream& operator<<(typename std::enable_if<std::is_enum<T>::value, std::os
static constexpr CAmount CENT{1000000};
+struct TestOpts {
+ std::vector<const char*> extra_args{};
+ bool coins_db_in_memory{true};
+ bool block_tree_db_in_memory{true};
+ bool setup_net{true};
+ bool setup_validation_interface{true};
+ bool min_validation_cache{false}; // Equivalent of -maxsigcachebytes=0
+};
+
/** Basic testing setup.
* This just configures logging, data dir and chain parameters.
*/
@@ -55,7 +65,7 @@ struct BasicTestingSetup {
util::SignalInterrupt m_interrupt;
node::NodeContext m_node; // keep as first member to be destructed last
- explicit BasicTestingSetup(const ChainType chainType = ChainType::MAIN, const std::vector<const char*>& extra_args = {});
+ explicit BasicTestingSetup(const ChainType chainType = ChainType::MAIN, TestOpts = {});
~BasicTestingSetup();
fs::path m_path_root;
@@ -72,8 +82,9 @@ struct ChainTestingSetup : public BasicTestingSetup {
node::CacheSizes m_cache_sizes{};
bool m_coins_db_in_memory{true};
bool m_block_tree_db_in_memory{true};
+ std::function<void()> m_make_chainman{};
- explicit ChainTestingSetup(const ChainType chainType = ChainType::MAIN, const std::vector<const char*>& extra_args = {});
+ explicit ChainTestingSetup(const ChainType chainType = ChainType::MAIN, TestOpts = {});
~ChainTestingSetup();
// Supplies a chainstate, if one is needed
@@ -85,9 +96,7 @@ struct ChainTestingSetup : public BasicTestingSetup {
struct TestingSetup : public ChainTestingSetup {
explicit TestingSetup(
const ChainType chainType = ChainType::MAIN,
- const std::vector<const char*>& extra_args = {},
- const bool coins_db_in_memory = true,
- const bool block_tree_db_in_memory = true);
+ TestOpts = {});
};
/** Identical to TestingSetup, but chain set to regtest */
@@ -106,9 +115,7 @@ class CScript;
struct TestChain100Setup : public TestingSetup {
TestChain100Setup(
const ChainType chain_type = ChainType::REGTEST,
- const std::vector<const char*>& extra_args = {},
- const bool coins_db_in_memory = true,
- const bool block_tree_db_in_memory = true);
+ TestOpts = {});
/**
* Create a new block with just given transactions, coinbase paying to
@@ -220,21 +227,23 @@ struct TestChain100Setup : public TestingSetup {
* be used in "hot loops", for example fuzzing or benchmarking.
*/
template <class T = const BasicTestingSetup>
-std::unique_ptr<T> MakeNoLogFileContext(const ChainType chain_type = ChainType::REGTEST, const std::vector<const char*>& extra_args = {})
+std::unique_ptr<T> MakeNoLogFileContext(const ChainType chain_type = ChainType::REGTEST, TestOpts opts = {})
{
- const std::vector<const char*> arguments = Cat(
+ opts.extra_args = Cat(
{
"-nodebuglogfile",
"-nodebug",
},
- extra_args);
+ opts.extra_args);
- return std::make_unique<T>(chain_type, arguments);
+ return std::make_unique<T>(chain_type, opts);
}
CBlock getBlock13b8a();
-// define an implicit conversion here so that uint256 may be used directly in BOOST_CHECK_*
+// Make types usable in BOOST_CHECK_*
+std::ostream& operator<<(std::ostream& os, const arith_uint256& num);
+std::ostream& operator<<(std::ostream& os, const uint160& num);
std::ostream& operator<<(std::ostream& os, const uint256& num);
/**
diff --git a/src/test/util/xoroshiro128plusplus.h b/src/test/util/xoroshiro128plusplus.h
deleted file mode 100644
index ac9f59b3f5..0000000000
--- a/src/test/util/xoroshiro128plusplus.h
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright (c) 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.
-
-#ifndef BITCOIN_TEST_UTIL_XOROSHIRO128PLUSPLUS_H
-#define BITCOIN_TEST_UTIL_XOROSHIRO128PLUSPLUS_H
-
-#include <cstdint>
-#include <limits>
-
-/** xoroshiro128++ PRNG. Extremely fast, not appropriate for cryptographic purposes.
- *
- * Memory footprint is 128bit, period is 2^128 - 1.
- * This class is not thread-safe.
- *
- * Reference implementation available at https://prng.di.unimi.it/xoroshiro128plusplus.c
- * See https://prng.di.unimi.it/
- */
-class XoRoShiRo128PlusPlus
-{
- uint64_t m_s0;
- uint64_t m_s1;
-
- [[nodiscard]] constexpr static uint64_t rotl(uint64_t x, int n)
- {
- return (x << n) | (x >> (64 - n));
- }
-
- [[nodiscard]] constexpr static uint64_t SplitMix64(uint64_t& seedval) noexcept
- {
- uint64_t z = (seedval += UINT64_C(0x9e3779b97f4a7c15));
- z = (z ^ (z >> 30U)) * UINT64_C(0xbf58476d1ce4e5b9);
- z = (z ^ (z >> 27U)) * UINT64_C(0x94d049bb133111eb);
- return z ^ (z >> 31U);
- }
-
-public:
- using result_type = uint64_t;
-
- constexpr explicit XoRoShiRo128PlusPlus(uint64_t seedval) noexcept
- : m_s0(SplitMix64(seedval)), m_s1(SplitMix64(seedval))
- {
- }
-
- // no copy - that is dangerous, we don't want accidentally copy the RNG and then have two streams
- // with exactly the same results. If you need a copy, call copy().
- XoRoShiRo128PlusPlus(const XoRoShiRo128PlusPlus&) = delete;
- XoRoShiRo128PlusPlus& operator=(const XoRoShiRo128PlusPlus&) = delete;
-
- // allow moves
- XoRoShiRo128PlusPlus(XoRoShiRo128PlusPlus&&) = default;
- XoRoShiRo128PlusPlus& operator=(XoRoShiRo128PlusPlus&&) = default;
-
- ~XoRoShiRo128PlusPlus() = default;
-
- constexpr result_type operator()() noexcept
- {
- uint64_t s0 = m_s0, s1 = m_s1;
- const uint64_t result = rotl(s0 + s1, 17) + s0;
- s1 ^= s0;
- m_s0 = rotl(s0, 49) ^ s1 ^ (s1 << 21);
- m_s1 = rotl(s1, 28);
- return result;
- }
-
- static constexpr result_type min() noexcept { return std::numeric_limits<result_type>::min(); }
- static constexpr result_type max() noexcept { return std::numeric_limits<result_type>::max(); }
- static constexpr double entropy() noexcept { return 0.0; }
-};
-
-#endif // BITCOIN_TEST_UTIL_XOROSHIRO128PLUSPLUS_H
diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp
index a371753adf..bf1fc1ea0a 100644
--- a/src/test/util_tests.cpp
+++ b/src/test/util_tests.cpp
@@ -58,7 +58,7 @@ static const std::string STRING_WITH_EMBEDDED_NULL_CHAR{"1"s "\0" "1"s};
/* defined in logging.cpp */
namespace BCLog {
- std::string LogEscapeMessage(const std::string& str);
+ std::string LogEscapeMessage(std::string_view str);
}
BOOST_FIXTURE_TEST_SUITE(util_tests, BasicTestingSetup)
@@ -459,7 +459,7 @@ BOOST_AUTO_TEST_CASE(util_IsHexNumber)
BOOST_AUTO_TEST_CASE(util_seed_insecure_rand)
{
- SeedInsecureRand(SeedRand::ZEROS);
+ SeedRandomForTest(SeedRand::ZEROS);
for (int mod=2;mod<11;mod++)
{
int mask = 1;
@@ -1508,8 +1508,10 @@ struct Tracker
Tracker(Tracker&& t) noexcept : origin(t.origin), copies(t.copies) {}
Tracker& operator=(const Tracker& t) noexcept
{
- origin = t.origin;
- copies = t.copies + 1;
+ if (this != &t) {
+ origin = t.origin;
+ copies = t.copies + 1;
+ }
return *this;
}
};
diff --git a/src/test/validation_chainstatemanager_tests.cpp b/src/test/validation_chainstatemanager_tests.cpp
index 1641c4cd22..cf819f8751 100644
--- a/src/test/validation_chainstatemanager_tests.cpp
+++ b/src/test/validation_chainstatemanager_tests.cpp
@@ -167,9 +167,10 @@ struct SnapshotTestSetup : TestChain100Setup {
// destructive filesystem operations.
SnapshotTestSetup() : TestChain100Setup{
{},
- {},
- /*coins_db_in_memory=*/false,
- /*block_tree_db_in_memory=*/false,
+ {
+ .coins_db_in_memory = false,
+ .block_tree_db_in_memory = false,
+ },
}
{
}
@@ -284,7 +285,7 @@ struct SnapshotTestSetup : TestChain100Setup {
const auto& au_data = ::Params().AssumeutxoForHeight(snapshot_height);
const CBlockIndex* tip = WITH_LOCK(chainman.GetMutex(), return chainman.ActiveTip());
- BOOST_CHECK_EQUAL(tip->nChainTx, au_data->nChainTx);
+ BOOST_CHECK_EQUAL(tip->m_chain_tx_count, au_data->m_chain_tx_count);
// To be checked against later when we try loading a subsequent snapshot.
uint256 loaded_snapshot_blockhash{*chainman.SnapshotBlockhash()};
@@ -464,7 +465,7 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager_loadblockindex, TestChain100Setup)
if (i < last_assumed_valid_idx && i >= assumed_valid_start_idx) {
index->nStatus = BlockStatus::BLOCK_VALID_TREE;
index->nTx = 0;
- index->nChainTx = 0;
+ index->m_chain_tx_count = 0;
}
++num_indexes;
diff --git a/src/test/validation_tests.cpp b/src/test/validation_tests.cpp
index 93a884be6d..2e378ed30b 100644
--- a/src/test/validation_tests.cpp
+++ b/src/test/validation_tests.cpp
@@ -143,11 +143,11 @@ BOOST_AUTO_TEST_CASE(test_assumeutxo)
const auto out110 = *params->AssumeutxoForHeight(110);
BOOST_CHECK_EQUAL(out110.hash_serialized.ToString(), "6657b736d4fe4db0cbc796789e812d5dba7f5c143764b1b6905612f1830609d1");
- BOOST_CHECK_EQUAL(out110.nChainTx, 111U);
+ BOOST_CHECK_EQUAL(out110.m_chain_tx_count, 111U);
- const auto out110_2 = *params->AssumeutxoForBlockhash(uint256S("0x696e92821f65549c7ee134edceeeeaaa4105647a3c4fd9f298c0aec0ab50425c"));
+ const auto out110_2 = *params->AssumeutxoForBlockhash(uint256{"696e92821f65549c7ee134edceeeeaaa4105647a3c4fd9f298c0aec0ab50425c"});
BOOST_CHECK_EQUAL(out110_2.hash_serialized.ToString(), "6657b736d4fe4db0cbc796789e812d5dba7f5c143764b1b6905612f1830609d1");
- BOOST_CHECK_EQUAL(out110_2.nChainTx, 111U);
+ BOOST_CHECK_EQUAL(out110_2.m_chain_tx_count, 111U);
}
BOOST_AUTO_TEST_CASE(block_malleation)
diff --git a/src/test/versionbits_tests.cpp b/src/test/versionbits_tests.cpp
index f462895edb..896840b0f3 100644
--- a/src/test/versionbits_tests.cpp
+++ b/src/test/versionbits_tests.cpp
@@ -419,7 +419,7 @@ BOOST_AUTO_TEST_CASE(versionbits_computeblockversion)
// check that any deployment on any chain can conceivably reach both
// ACTIVE and FAILED states in roughly the way we expect
- for (const auto& chain_type: {ChainType::MAIN, ChainType::TESTNET, ChainType::SIGNET, ChainType::REGTEST}) {
+ for (const auto& chain_type: {ChainType::MAIN, ChainType::TESTNET, ChainType::TESTNET4, ChainType::SIGNET, ChainType::REGTEST}) {
const auto chainParams = CreateChainParams(*m_node.args, chain_type);
uint32_t chain_all_vbits{0};
for (int i = 0; i < (int)Consensus::MAX_VERSION_BITS_DEPLOYMENTS; ++i) {
diff --git a/src/test/xoroshiro128plusplus_tests.cpp b/src/test/xoroshiro128plusplus_tests.cpp
deleted file mode 100644
index ea1b3e355f..0000000000
--- a/src/test/xoroshiro128plusplus_tests.cpp
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright (c) 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 <test/util/setup_common.h>
-#include <test/util/xoroshiro128plusplus.h>
-
-#include <boost/test/unit_test.hpp>
-
-BOOST_FIXTURE_TEST_SUITE(xoroshiro128plusplus_tests, BasicTestingSetup)
-
-BOOST_AUTO_TEST_CASE(reference_values)
-{
- // numbers generated from reference implementation
- XoRoShiRo128PlusPlus rng(0);
- BOOST_TEST(0x6f68e1e7e2646ee1 == rng());
- BOOST_TEST(0xbf971b7f454094ad == rng());
- BOOST_TEST(0x48f2de556f30de38 == rng());
- BOOST_TEST(0x6ea7c59f89bbfc75 == rng());
-
- // seed with a random number
- rng = XoRoShiRo128PlusPlus(0x1a26f3fa8546b47a);
- BOOST_TEST(0xc8dc5e08d844ac7d == rng());
- BOOST_TEST(0x5b5f1f6d499dad1b == rng());
- BOOST_TEST(0xbeb0031f93313d6f == rng());
- BOOST_TEST(0xbfbcf4f43a264497 == rng());
-}
-
-BOOST_AUTO_TEST_SUITE_END()