diff options
Diffstat (limited to 'src/test')
25 files changed, 885 insertions, 382 deletions
diff --git a/src/test/addrman_tests.cpp b/src/test/addrman_tests.cpp index b01ba81c5f..bfefc3ff97 100644 --- a/src/test/addrman_tests.cpp +++ b/src/test/addrman_tests.cpp @@ -98,8 +98,8 @@ BOOST_AUTO_TEST_CASE(addrman_simple) // Test: reset addrman and test AddrMan::Add multiple addresses works as expected addrman = std::make_unique<AddrMan>(EMPTY_NETGROUPMAN, DETERMINISTIC, GetCheckRatio(m_node)); std::vector<CAddress> vAddr; - vAddr.push_back(CAddress(ResolveService("250.1.1.3", 8333), NODE_NONE)); - vAddr.push_back(CAddress(ResolveService("250.1.1.4", 8333), NODE_NONE)); + vAddr.emplace_back(ResolveService("250.1.1.3", 8333), NODE_NONE); + vAddr.emplace_back(ResolveService("250.1.1.4", 8333), NODE_NONE); BOOST_CHECK(addrman->Add(vAddr, source)); BOOST_CHECK(addrman->Size() >= 1); } diff --git a/src/test/bip32_tests.cpp b/src/test/bip32_tests.cpp index fe3d8995e9..cbbec94e00 100644 --- a/src/test/bip32_tests.cpp +++ b/src/test/bip32_tests.cpp @@ -29,7 +29,7 @@ struct TestVector { explicit TestVector(std::string strHexMasterIn) : strHexMaster(strHexMasterIn) {} TestVector& operator()(std::string pub, std::string prv, unsigned int nChild) { - vDerive.push_back(TestDerivation()); + vDerive.emplace_back(); TestDerivation &der = vDerive.back(); der.pub = pub; der.prv = prv; diff --git a/src/test/descriptor_tests.cpp b/src/test/descriptor_tests.cpp index 3a30ef453e..f4f4e39f40 100644 --- a/src/test/descriptor_tests.cpp +++ b/src/test/descriptor_tests.cpp @@ -384,6 +384,54 @@ void Check(const std::string& prv, const std::string& pub, const std::string& no } } +void CheckInferDescriptor(const std::string& script_hex, const std::string& expected_desc, const std::vector<std::string>& hex_scripts, const std::vector<std::pair<std::string, std::string>>& origin_pubkeys) +{ + std::vector<unsigned char> script_bytes{ParseHex(script_hex)}; + const CScript& script{script_bytes.begin(), script_bytes.end()}; + + FlatSigningProvider provider; + for (const std::string& prov_script_hex : hex_scripts) { + std::vector<unsigned char> prov_script_bytes{ParseHex(prov_script_hex)}; + const CScript& prov_script{prov_script_bytes.begin(), prov_script_bytes.end()}; + provider.scripts.emplace(CScriptID(prov_script), prov_script); + } + for (const auto& [pubkey_hex, origin_str] : origin_pubkeys) { + CPubKey origin_pubkey{ParseHex(pubkey_hex)}; + provider.pubkeys.emplace(origin_pubkey.GetID(), origin_pubkey); + + if (!origin_str.empty()) { + using namespace spanparsing; + KeyOriginInfo info; + Span<const char> origin_sp{origin_str}; + std::vector<Span<const char>> origin_split = Split(origin_sp, "/"); + std::string fpr_str(origin_split[0].begin(), origin_split[0].end()); + auto fpr_bytes = ParseHex(fpr_str); + std::copy(fpr_bytes.begin(), fpr_bytes.end(), info.fingerprint); + for (size_t i = 1; i < origin_split.size(); ++i) { + Span<const char> elem = origin_split[i]; + bool hardened = false; + if (elem.size() > 0) { + const char last = elem[elem.size() - 1]; + if (last == '\'' || last == 'h') { + elem = elem.first(elem.size() - 1); + hardened = true; + } + } + uint32_t p; + assert(ParseUInt32(std::string(elem.begin(), elem.end()), &p)); + info.path.push_back(p | (((uint32_t)hardened) << 31)); + } + + provider.origins.emplace(origin_pubkey.GetID(), std::make_pair(origin_pubkey, info)); + } + } + + std::string checksum{GetDescriptorChecksum(expected_desc)}; + + std::unique_ptr<Descriptor> desc = InferDescriptor(script, provider); + BOOST_CHECK_EQUAL(desc->ToString(), expected_desc + "#" + checksum); +} + } BOOST_FIXTURE_TEST_SUITE(descriptor_tests, BasicTestingSetup) @@ -409,6 +457,11 @@ BOOST_AUTO_TEST_CASE(descriptor_test) 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 + // Equivalent single-key hybrid is not allowed + CheckUnparsable("", "combo(07a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "combo(): Hybrid public keys are not allowed"); + CheckUnparsable("", "pk(0623542d61708e3fc48ba78fbe8fcc983ba94a520bc33f82b8e45e51dbc47af2726bcf181925eee1bdd868b109314f3ea92a6fc23d6b66057d3acfba04d6b08b58)", "pk(): Hybrid public keys are not allowed"); + CheckUnparsable("", "pkh(07a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "pkh(): Hybrid public keys are not allowed"); + // Some unconventional single-key constructions Check("sh(pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "sh(pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", "sh(pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", SIGNABLE, {{"a9141857af51a5e516552b3086430fd8ce55f7c1a52487"}}, OutputType::LEGACY); Check("sh(pkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "sh(pkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", "sh(pkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", SIGNABLE, {{"a9141a31ad23bf49c247dd531a623c2ef57da3c400c587"}}, OutputType::LEGACY); @@ -533,11 +586,15 @@ BOOST_AUTO_TEST_CASE(descriptor_test) // Invalid checksum CheckUnparsable("wsh(and_v(vc:andor(pk(L4gM1FBdyHNpkzsFh9ipnofLhpZRp2mwobpeULy1a6dBTvw8Ywtd),pk_k(Kx9HCDjGiwFcgVNhTrS5z5NeZdD6veeam61eDxLDCkGWujvL4Gnn),and_v(v:older(1),pk_k(L4o2kDvXXDRH2VS9uBnouScLduWt4dZnM25se7kvEjJeQ285en2A))),after(10)))#abcdef12", "wsh(and_v(vc:andor(pk(03cdabb7f2dce7bfbd8a0b9570c6fd1e712e5d64045e9d6b517b3d5072251dc204),pk_k(032707170c71d8f75e4ca4e3fce870b9409dcaf12b051d3bcadff74747fa7619c0),and_v(v:older(1),pk_k(02aa27e5eb2c185e87cd1dbc3e0efc9cb1175235e0259df1713424941c3cb40402))),after(10)))#abcdef12", "Provided checksum 'abcdef12' does not match computed checksum 'tyzp6a7p'"); - // Only p2wsh context is valid - CheckUnparsable("sh(and_v(vc:andor(pk(L4gM1FBdyHNpkzsFh9ipnofLhpZRp2mwobpeULy1a6dBTvw8Ywtd),pk_k(Kx9HCDjGiwFcgVNhTrS5z5NeZdD6veeam61eDxLDCkGWujvL4Gnn),and_v(v:older(1),pk_k(L4o2kDvXXDRH2VS9uBnouScLduWt4dZnM25se7kvEjJeQ285en2A))),after(10)))", "sh(and_v(vc:andor(pk(03cdabb7f2dce7bfbd8a0b9570c6fd1e712e5d64045e9d6b517b3d5072251dc204),pk_k(032707170c71d8f75e4ca4e3fce870b9409dcaf12b051d3bcadff74747fa7619c0),and_v(v:older(1),pk_k(02aa27e5eb2c185e87cd1dbc3e0efc9cb1175235e0259df1713424941c3cb40402))),after(10)))", "Miniscript expressions can only be used in wsh"); + // Only p2wsh or tr contexts are valid + CheckUnparsable("sh(and_v(vc:andor(pk(L4gM1FBdyHNpkzsFh9ipnofLhpZRp2mwobpeULy1a6dBTvw8Ywtd),pk_k(Kx9HCDjGiwFcgVNhTrS5z5NeZdD6veeam61eDxLDCkGWujvL4Gnn),and_v(v:older(1),pk_k(L4o2kDvXXDRH2VS9uBnouScLduWt4dZnM25se7kvEjJeQ285en2A))),after(10)))", "sh(and_v(vc:andor(pk(03cdabb7f2dce7bfbd8a0b9570c6fd1e712e5d64045e9d6b517b3d5072251dc204),pk_k(032707170c71d8f75e4ca4e3fce870b9409dcaf12b051d3bcadff74747fa7619c0),and_v(v:older(1),pk_k(02aa27e5eb2c185e87cd1dbc3e0efc9cb1175235e0259df1713424941c3cb40402))),after(10)))", "Miniscript expressions can only be used in wsh or tr."); CheckUnparsable("tr(and_v(vc:andor(pk(L4gM1FBdyHNpkzsFh9ipnofLhpZRp2mwobpeULy1a6dBTvw8Ywtd),pk_k(Kx9HCDjGiwFcgVNhTrS5z5NeZdD6veeam61eDxLDCkGWujvL4Gnn),and_v(v:older(1),pk_k(L4o2kDvXXDRH2VS9uBnouScLduWt4dZnM25se7kvEjJeQ285en2A))),after(10)))", "tr(and_v(vc:andor(pk(03cdabb7f2dce7bfbd8a0b9570c6fd1e712e5d64045e9d6b517b3d5072251dc204),pk_k(032707170c71d8f75e4ca4e3fce870b9409dcaf12b051d3bcadff74747fa7619c0),and_v(v:older(1),pk_k(02aa27e5eb2c185e87cd1dbc3e0efc9cb1175235e0259df1713424941c3cb40402))),after(10)))", "tr(): key 'and_v(vc:andor(pk(03cdabb7f2dce7bfbd8a0b9570c6fd1e712e5d64045e9d6b517b3d5072251dc204),pk_k(032707170c71d8f75e4ca4e3fce870b9409dcaf12b051d3bcadff74747fa7619c0),and_v(v:older(1),pk_k(02aa27e5eb2c185e87cd1dbc3e0efc9cb1175235e0259df1713424941c3cb40402))),after(10))' is not valid"); - CheckUnparsable("raw(and_v(vc:andor(pk(L4gM1FBdyHNpkzsFh9ipnofLhpZRp2mwobpeULy1a6dBTvw8Ywtd),pk_k(Kx9HCDjGiwFcgVNhTrS5z5NeZdD6veeam61eDxLDCkGWujvL4Gnn),and_v(v:older(1),pk_k(L4o2kDvXXDRH2VS9uBnouScLduWt4dZnM25se7kvEjJeQ285en2A))),after(10)))", "sh(and_v(vc:andor(pk(03cdabb7f2dce7bfbd8a0b9570c6fd1e712e5d64045e9d6b517b3d5072251dc204),pk_k(032707170c71d8f75e4ca4e3fce870b9409dcaf12b051d3bcadff74747fa7619c0),and_v(v:older(1),pk_k(02aa27e5eb2c185e87cd1dbc3e0efc9cb1175235e0259df1713424941c3cb40402))),after(10)))", "Miniscript expressions can only be used in wsh"); + CheckUnparsable("raw(and_v(vc:andor(pk(L4gM1FBdyHNpkzsFh9ipnofLhpZRp2mwobpeULy1a6dBTvw8Ywtd),pk_k(Kx9HCDjGiwFcgVNhTrS5z5NeZdD6veeam61eDxLDCkGWujvL4Gnn),and_v(v:older(1),pk_k(L4o2kDvXXDRH2VS9uBnouScLduWt4dZnM25se7kvEjJeQ285en2A))),after(10)))", "sh(and_v(vc:andor(pk(03cdabb7f2dce7bfbd8a0b9570c6fd1e712e5d64045e9d6b517b3d5072251dc204),pk_k(032707170c71d8f75e4ca4e3fce870b9409dcaf12b051d3bcadff74747fa7619c0),and_v(v:older(1),pk_k(02aa27e5eb2c185e87cd1dbc3e0efc9cb1175235e0259df1713424941c3cb40402))),after(10)))", "Miniscript expressions can only be used in wsh or tr."); CheckUnparsable("", "tr(034D2224bbbbbbbbbbcbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb40,{{{{{{{{{{{{{{{{{{{{{{multi(1,xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc/967808'/9,xprvA1RpRA33e1JQ7ifknakTFNpgXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc/968/2/5/8/5/2/5/58/58/2/5/5/5/58/588/2/6/8/5/2/8/2/5/8/2/58/2/5/8/5/2/8/5/8/3/4/5/58/55/2/5/58/58/2/5/5/5/8/5/2/8/5/85/2/8/2/5/8/5/2/5/58/58/2/5/58/58/588/2/58/2/8/5/8/5/4/5/585/2/5/58/58/2/5/5/58/588/2/58/2/5/8/5/2/8/2/5/8/5/5/58/588/2/6/8/5/2/8/2/5/8/5/2/5/58/58/2/5/58/58/2/0/8/5/2/8/5/8/5/4/5/58/588/2/6/8/5/2/8/2/5/8/5/2/5/58/58/2/5/58/58/588/2/58/2/5/8/5/8/24/5/58/52/5/8/5/2/8/24/5/58/588/246/8/5/2/8/2/5/8/5/2/5/58/58/2/5/5/5/58/588/2/6/8/5/2/8/2/5/8/2/58/2/5/8/5/2/8/5/8/5/4/5/58/55/58/2/5/8/55/2/5/8/58/555/58/2/5/8/4//2/5/58/5w/2/5/8/5/2/4/5/58/5558'/2/5/58/58/2/5/5/58/588/2/58/2/5/8/5/2/8/2/5/8/5/5/8/58/2/5/58/58/2/5/8/9/588/2/58/2/5/8/5/2/8/5/8/5/4/5/58/588/2/6/8/5/2/8/2/5/8/5/2/5/58/58/2/5/5/58/588/2/58/2/5/8/5/2/82/5/8/5/5/58/52/6/8/5/2/8/{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{}{{{{{{{{{DDD2/5/8/5/2/5/58/58/2/5/58/58/588/2/58/2/8/5/8/5/4/5/58/588/2/6/8/5/2/8/2/5/8588/246/8/5/2DLDDDDDDDbbD3DDDD/8/2/5/8/5/2/5/58/58/2/5/5/5/58/588/2/6/8/5/2/8/2/5/8/2/58/2/5/8/5/2/8/5/8/3/4/5/58/55/2/5/58/58/2/5/5/5/8/5/2/8/5/85/2/8/2/5/8D)/5/2/5/58/58/2/5/58/58/58/588/2/58/2/5/8/5/25/58/58/2/5/58/58/2/5/8/9/588/2/58/2/6780,xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFW/8/5/2/5/58678008')", "'multi(1,xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc/967808'/9,xprvA1RpRA33e1JQ7ifknakTFNpgXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc/968/2/5/8/5/2/5/58/58/2/5/5/5/58/588/2/6/8/5/2/8/2/5/8/2/58/2/5/8/5/2/8/5/8/3/4/5/58/55/2/5/58/58/2/5/5/5/8/5/2/8/5/85/2/8/2/5/8/5/2/5/58/58/2/5/58/58/588/2/58/2/8/5/8/5/4/5/585/2/5/58/58/2/5/5/58/588/2/58/2/5/8/5/2/8/2/5/8/5/5/58/588/2/6/8/5/2/8/2/5/8/5/2/5/58/58/2/5/58/58/2/0/8/5/2/8/5/8/5/4/5/58/588/2/6/8/5/2/8/2/5/8/5/2/5/58/58/2/5/58/58/588/2/58/2/5/8/5/8/24/5/58/52/5/8/5/2/8/24/5/58/588/246/8/5/2/8/2/5/8/5/2/5/58/58/2/5/5/5/58/588/2/6/8/5/2/8/2/5/8/2/58/2/5/8/5/2/8/5/8/5/4/5/58/55/58/2/5/8/55/2/5/8/58/555/58/2/5/8/4//2/5/58/5w/2/5/8/5/2/4/5/58/5558'/2/5/58/58/2/5/5/58/588/2/58/2/5/8/5/2/8/2/5/8/5/5/8/58/2/5/58/58/2/5/8/9/588/2/58/2/5/8/5/2/8/5/8/5/4/5/58/588/2/6/8/5/2/8/2/5/8/5/2/5/58/58/2/5/5/58/588/2/58/2/5/8/5/2/82/5/8/5/5/58/52/6/8/5/2/8/{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{}{{{{{{{{{DDD2/5/8/5/2/5/58/58/2/5/58/58/588/2/58/2/8/5/8/5/4/5/58/588/2/6/8/5/2/8/2/5/8588/246/8/5/2DLDDDDDDDbbD3DDDD/8/2/5/8/5/2/5/58/58/2/5/5/5/58/588/2/6/8/5/2/8/2/5/8/2/58/2/5/8/5/2/8/5/8/3/4/5/58/55/2/5/58/58/2/5/5/5/8/5/2/8/5/85/2/8/2/5/8D)/5/2/5/58/58/2/5/58/58/58/588/2/58/2/5/8/5/25/58/58/2/5/58/58/2/5/8/9/588/2/58/2/6780,xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFW/8/5/2/5/58678008'' is not a valid descriptor function"); + // No uncompressed keys allowed + CheckUnparsable("", "wsh(and_v(vc:andor(pk(03cdabb7f2dce7bfbd8a0b9570c6fd1e712e5d64045e9d6b517b3d5072251dc204),pk_k(032707170c71d8f75e4ca4e3fce870b9409dcaf12b051d3bcadff74747fa7619c0),and_v(v:older(1),pk_k(049228de6902abb4f541791f6d7f925b10e2078ccb1298856e5ea5cc5fd667f930eac37a00cc07f9a91ef3c2d17bf7a17db04552ff90ac312a5b8b4caca6c97aa4))),after(10)))", "A function is needed within P2WSH"); + // No hybrid keys allowed + CheckUnparsable("", "wsh(and_v(vc:andor(pk(03cdabb7f2dce7bfbd8a0b9570c6fd1e712e5d64045e9d6b517b3d5072251dc204),pk_k(032707170c71d8f75e4ca4e3fce870b9409dcaf12b051d3bcadff74747fa7619c0),and_v(v:older(1),pk_k(069228de6902abb4f541791f6d7f925b10e2078ccb1298856e5ea5cc5fd667f930eac37a00cc07f9a91ef3c2d17bf7a17db04552ff90ac312a5b8b4caca6c97aa4))),after(10)))", "A function is needed within P2WSH"); // Insane at top level CheckUnparsable("wsh(and_b(vc:andor(pk(L4gM1FBdyHNpkzsFh9ipnofLhpZRp2mwobpeULy1a6dBTvw8Ywtd),pk_k(Kx9HCDjGiwFcgVNhTrS5z5NeZdD6veeam61eDxLDCkGWujvL4Gnn),and_v(v:older(1),pk_k(L4o2kDvXXDRH2VS9uBnouScLduWt4dZnM25se7kvEjJeQ285en2A))),after(10)))", "wsh(and_b(vc:andor(pk(03cdabb7f2dce7bfbd8a0b9570c6fd1e712e5d64045e9d6b517b3d5072251dc204),pk_k(032707170c71d8f75e4ca4e3fce870b9409dcaf12b051d3bcadff74747fa7619c0),and_v(v:older(1),pk_k(02aa27e5eb2c185e87cd1dbc3e0efc9cb1175235e0259df1713424941c3cb40402))),after(10)))", "and_b(vc:andor(pk(03cdabb7f2dce7bfbd8a0b9570c6fd1e712e5d64045e9d6b517b3d5072251dc204),pk_k(032707170c71d8f75e4ca4e3fce870b9409dcaf12b051d3bcadff74747fa7619c0),and_v(v:older(1),pk_k(02aa27e5eb2c185e87cd1dbc3e0efc9cb1175235e0259df1713424941c3cb40402))),after(10)) is invalid"); // Invalid sub @@ -573,6 +630,31 @@ BOOST_AUTO_TEST_CASE(descriptor_test) // 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")}}); + // 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). + Check("tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,pkh(L1NKM8dVA1h52mwDrmk1YreTWkAZZTu2vmKLpmLEbFRqGQYjHeEV))", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,pkh(30a6069f344fb784a2b4c99540a91ee727c91e3a25ef6aae867d9c65b5f23529))", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,pkh(30a6069f344fb784a2b4c99540a91ee727c91e3a25ef6aae867d9c65b5f23529))", MISSING_PRIVKEYS | XONLY_KEYS | SIGNABLE, {{"51201e9875f690f5847404e4c5951e2f029887df0525691ee11a682afd37b608aad4"}}, OutputType::BECH32M); + // Can have a Miniscript expression under tr() if it's part of a tree. + Check("tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,{{pkh(KykUPmR5967F4URzMUeCv9kNMU9CNRWycrPmx3ZvfkWoQLabbimL),pk(L3Enys1jFgTq4E24b8Uom1kAz6cNkz3Z82XZpBKCE2ztErq9fqvJ)},thresh(1,pk(L1NKM8dVA1h52mwDrmk1YreTWkAZZTu2vmKLpmLEbFRqGQYjHeEV),s:pk(Kz3iCBy3HNGP5CZWDsAMmnCMFNwqdDohudVN9fvkrN7tAkzKNtM7))})", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,{{pkh(1c9bc926084382e76da33b5a52d17b1fa153c072aae5fb5228ecc2ccf89d79d5),pk(0dd6b52b192ab195558d22dd8437a9ec4519ee5ded496c0d55bc9b1a8b0e8c2b)},thresh(1,pk(30a6069f344fb784a2b4c99540a91ee727c91e3a25ef6aae867d9c65b5f23529),s:pk(9918d400c1b8c3c478340a40117ced4054b6b58f48cdb3c89b836bdfee1f5766))})", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,{{pkh(1c9bc926084382e76da33b5a52d17b1fa153c072aae5fb5228ecc2ccf89d79d5),pk(0dd6b52b192ab195558d22dd8437a9ec4519ee5ded496c0d55bc9b1a8b0e8c2b)},thresh(1,pk(30a6069f344fb784a2b4c99540a91ee727c91e3a25ef6aae867d9c65b5f23529),s:pk(9918d400c1b8c3c478340a40117ced4054b6b58f48cdb3c89b836bdfee1f5766))})", MISSING_PRIVKEYS | XONLY_KEYS, {{"5120d8ea39b29de2b550b68bd2ada8b075c888c2b2df3290c7a35856482747848934"}}, OutputType::BECH32M); + // Can have two Miniscripts in a Taproot with mixed private and public keys, and mixed ranged extended keys and raw keys. + Check("tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,{and_v(v:pk(xpub6AGbgdKcAGeUWaGNKH2o3sRvjtvJCGZ1NwrHqMJDwD4bN1QuwPQSsdeAYkPZGPt2FTAyu6nWGsC3fN2nsBELrLPcRNuwwr5k1X7yW5WV4aX/*),pk(02daf6e3477fc3906a1997820ed2940c8f5fa0942946d0368f981b001fdd85afcb)),and_v(v:pk(xprv9wCN7tTqN5ATsmBGEijuNeUgQjma9tv3GmdWLmbYiuArPsAMj6tD1uASiBfm47kdoi7bDBAVxUZNLM2MkeouPK5menDTyCNZtExQrKhVu7C/*),pk(03272c0c1ae2c07528283b91ca57b45d2cc84e7960e1f17f58815372285f35e99a))})", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,{and_v(v:pk(xpub6AGbgdKcAGeUWaGNKH2o3sRvjtvJCGZ1NwrHqMJDwD4bN1QuwPQSsdeAYkPZGPt2FTAyu6nWGsC3fN2nsBELrLPcRNuwwr5k1X7yW5WV4aX/*),pk(02daf6e3477fc3906a1997820ed2940c8f5fa0942946d0368f981b001fdd85afcb)),and_v(v:pk(xpub6ABiXPzjCSim6FFjLkGujnRQxmc4ZMdtdzZ79A1AHEhqGfVWGeCTZhUvZTSf1mNnGUtyNqgfE9eWaYdYReDKbPYqgqi9LLVZSmWnLQRx477/*),pk(03272c0c1ae2c07528283b91ca57b45d2cc84e7960e1f17f58815372285f35e99a))})", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,{and_v(v:pk(xpub6AGbgdKcAGeUWaGNKH2o3sRvjtvJCGZ1NwrHqMJDwD4bN1QuwPQSsdeAYkPZGPt2FTAyu6nWGsC3fN2nsBELrLPcRNuwwr5k1X7yW5WV4aX/*),pk(02daf6e3477fc3906a1997820ed2940c8f5fa0942946d0368f981b001fdd85afcb)),and_v(v:pk(xpub6ABiXPzjCSim6FFjLkGujnRQxmc4ZMdtdzZ79A1AHEhqGfVWGeCTZhUvZTSf1mNnGUtyNqgfE9eWaYdYReDKbPYqgqi9LLVZSmWnLQRx477/*),pk(03272c0c1ae2c07528283b91ca57b45d2cc84e7960e1f17f58815372285f35e99a))})", MISSING_PRIVKEYS | XONLY_KEYS | RANGE | MIXED_PUBKEYS, {{"5120793185cd1a9a0bb710fa57df3845ac4ddf7df63b74beadce2573cbb0b508b3a4"}}, OutputType::BECH32M, /*op_desc_id=*/{}, {{}, {0}}); + // Can sign for a Miniscript expression containing a hash challenge inside a Taproot tree. (Fails without the + // preimages and the sequence, passes with.) + Check("tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,{and_v(and_v(v:hash256(ae253ca2a54debcac7ecf414f6734f48c56421a08bb59182ff9f39a6fffdb588),v:pk(KykUPmR5967F4URzMUeCv9kNMU9CNRWycrPmx3ZvfkWoQLabbimL)),older(42)),multi_a(2,adf586a32ad4b0674a86022b000348b681b4c97a811f67eefe4a6e066e55080c,KztMyyi1pXUtuZfJSB7JzVdmJMAz7wfGVFoSRUR5CVZxXxULXuGR)})", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,{and_v(and_v(v:hash256(ae253ca2a54debcac7ecf414f6734f48c56421a08bb59182ff9f39a6fffdb588),v:pk(1c9bc926084382e76da33b5a52d17b1fa153c072aae5fb5228ecc2ccf89d79d5)),older(42)),multi_a(2,adf586a32ad4b0674a86022b000348b681b4c97a811f67eefe4a6e066e55080c,14fa4ad085cdee1e2fc73d491b36a96c192382b1d9a21108eb3533f630364f9f)})", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,{and_v(and_v(v:hash256(ae253ca2a54debcac7ecf414f6734f48c56421a08bb59182ff9f39a6fffdb588),v:pk(1c9bc926084382e76da33b5a52d17b1fa153c072aae5fb5228ecc2ccf89d79d5)),older(42)),multi_a(2,adf586a32ad4b0674a86022b000348b681b4c97a811f67eefe4a6e066e55080c,14fa4ad085cdee1e2fc73d491b36a96c192382b1d9a21108eb3533f630364f9f)})", MISSING_PRIVKEYS | XONLY_KEYS | SIGNABLE | SIGNABLE_FAILS, {{"51209a3d79db56fbe3ba4d905d827b62e1ed31cd6df1198b8c759d589c0f4efc27bd"}}, OutputType::BECH32M); + Check("tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,{and_v(and_v(v:hash256(ae253ca2a54debcac7ecf414f6734f48c56421a08bb59182ff9f39a6fffdb588),v:pk(KykUPmR5967F4URzMUeCv9kNMU9CNRWycrPmx3ZvfkWoQLabbimL)),older(42)),multi_a(2,adf586a32ad4b0674a86022b000348b681b4c97a811f67eefe4a6e066e55080c,KztMyyi1pXUtuZfJSB7JzVdmJMAz7wfGVFoSRUR5CVZxXxULXuGR)})", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,{and_v(and_v(v:hash256(ae253ca2a54debcac7ecf414f6734f48c56421a08bb59182ff9f39a6fffdb588),v:pk(1c9bc926084382e76da33b5a52d17b1fa153c072aae5fb5228ecc2ccf89d79d5)),older(42)),multi_a(2,adf586a32ad4b0674a86022b000348b681b4c97a811f67eefe4a6e066e55080c,14fa4ad085cdee1e2fc73d491b36a96c192382b1d9a21108eb3533f630364f9f)})", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,{and_v(and_v(v:hash256(ae253ca2a54debcac7ecf414f6734f48c56421a08bb59182ff9f39a6fffdb588),v:pk(1c9bc926084382e76da33b5a52d17b1fa153c072aae5fb5228ecc2ccf89d79d5)),older(42)),multi_a(2,adf586a32ad4b0674a86022b000348b681b4c97a811f67eefe4a6e066e55080c,14fa4ad085cdee1e2fc73d491b36a96c192382b1d9a21108eb3533f630364f9f)})", MISSING_PRIVKEYS | XONLY_KEYS | SIGNABLE, {{"51209a3d79db56fbe3ba4d905d827b62e1ed31cd6df1198b8c759d589c0f4efc27bd"}}, OutputType::BECH32M, /*op_desc_id=*/{}, {{}}, /*spender_nlocktime=*/0, /*spender_nsequence=*/42, /*preimages=*/{{ParseHex("ae253ca2a54debcac7ecf414f6734f48c56421a08bb59182ff9f39a6fffdb588"), ParseHex("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f")}}); + + // Basic sh(pkh()) with key origin + CheckInferDescriptor("a9141a31ad23bf49c247dd531a623c2ef57da3c400c587", "sh(pkh([deadbeef/0h/0h/0]03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", {"76a9149a1c78a507689f6f54b847ad1cef1e614ee23f1e88ac"}, {{"03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd", "deadbeef/0h/0h/0"}}); + // p2pk script with hybrid key must infer as raw() + CheckInferDescriptor("41069228de6902abb4f541791f6d7f925b10e2078ccb1298856e5ea5cc5fd667f930eac37a00cc07f9a91ef3c2d17bf7a17db04552ff90ac312a5b8b4caca6c97aa4ac", "raw(41069228de6902abb4f541791f6d7f925b10e2078ccb1298856e5ea5cc5fd667f930eac37a00cc07f9a91ef3c2d17bf7a17db04552ff90ac312a5b8b4caca6c97aa4ac)", {}, {{"069228de6902abb4f541791f6d7f925b10e2078ccb1298856e5ea5cc5fd667f930eac37a00cc07f9a91ef3c2d17bf7a17db04552ff90ac312a5b8b4caca6c97aa4", ""}}); + // p2pkh script with hybrid key must infer as addr() + CheckInferDescriptor("76a91445ff7c2327866472639d507334a9a00119dfd32688ac", "addr(17P7ge56F2QcdHxxRBa2NyzmejFggPwBJ9)", {}, {{"069228de6902abb4f541791f6d7f925b10e2078ccb1298856e5ea5cc5fd667f930eac37a00cc07f9a91ef3c2d17bf7a17db04552ff90ac312a5b8b4caca6c97aa4", ""}}); + // p2wpkh script with uncompressed key must infer as addr() + CheckInferDescriptor("001422e363a523947a110d9a9eb114820de183aca313", "addr(bc1qyt3k8ffrj3apzrv6n6c3fqsduxp6egcnk2r66j)", {}, {{"049228de6902abb4f541791f6d7f925b10e2078ccb1298856e5ea5cc5fd667f930eac37a00cc07f9a91ef3c2d17bf7a17db04552ff90ac312a5b8b4caca6c97aa4", ""}}); + // Infer pkh() from p2pkh with uncompressed key + CheckInferDescriptor("76a914a31725c74421fadc50d35520ab8751ed120af80588ac", "pkh(04c56fe4a92d401bcbf1b3dfbe4ac3dac5602ca155a3681497f02c1b9a733b92d704e2da6ec4162e4846af9236ef4171069ac8b7f8234a8405b6cadd96f34f5a31)", {}, {{"04c56fe4a92d401bcbf1b3dfbe4ac3dac5602ca155a3681497f02c1b9a733b92d704e2da6ec4162e4846af9236ef4171069ac8b7f8234a8405b6cadd96f34f5a31", ""}}); + // Infer pk() from p2pk with uncompressed key + CheckInferDescriptor("4104032540df1d3c7070a8ab3a9cdd304dfc7fd1e6541369c53c4c3310b2537d91059afc8b8e7673eb812a32978dabb78c40f2e423f7757dca61d11838c7aeeb5220ac", "pk(04032540df1d3c7070a8ab3a9cdd304dfc7fd1e6541369c53c4c3310b2537d91059afc8b8e7673eb812a32978dabb78c40f2e423f7757dca61d11838c7aeeb5220)", {}, {{"04032540df1d3c7070a8ab3a9cdd304dfc7fd1e6541369c53c4c3310b2537d91059afc8b8e7673eb812a32978dabb78c40f2e423f7757dca61d11838c7aeeb5220", ""}}); } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/fuzz/chain.cpp b/src/test/fuzz/chain.cpp index 49b9898228..0363f317b6 100644 --- a/src/test/fuzz/chain.cpp +++ b/src/test/fuzz/chain.cpp @@ -29,7 +29,7 @@ FUZZ_TARGET(chain) (void)disk_block_index->GetBlockTimeMax(); (void)disk_block_index->GetMedianTimePast(); (void)disk_block_index->GetUndoPos(); - (void)disk_block_index->HaveTxsDownloaded(); + (void)disk_block_index->HaveNumChainTxs(); (void)disk_block_index->IsValid(); } diff --git a/src/test/fuzz/fees.cpp b/src/test/fuzz/fees.cpp index deb0ed65ca..38a8c6798e 100644 --- a/src/test/fuzz/fees.cpp +++ b/src/test/fuzz/fees.cpp @@ -17,7 +17,8 @@ FUZZ_TARGET(fees) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); const CFeeRate minimal_incremental_fee{ConsumeMoney(fuzzed_data_provider)}; - FeeFilterRounder fee_filter_rounder{minimal_incremental_fee}; + FastRandomContext rng{/*fDeterministic=*/true}; + FeeFilterRounder fee_filter_rounder{minimal_incremental_fee, rng}; LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 10000) { const CAmount current_minimum_fee = ConsumeMoney(fuzzed_data_provider); const CAmount rounded_fee = fee_filter_rounder.round(current_minimum_fee); diff --git a/src/test/fuzz/mini_miner.cpp b/src/test/fuzz/mini_miner.cpp index e17e2bad60..2f53943c31 100644 --- a/src/test/fuzz/mini_miner.cpp +++ b/src/test/fuzz/mini_miner.cpp @@ -25,7 +25,7 @@ void initialize_miner() static const auto testing_setup = MakeNoLogFileContext<const TestingSetup>(); g_setup = testing_setup.get(); for (uint32_t i = 0; i < uint32_t{100}; ++i) { - g_available_coins.push_back(COutPoint{uint256::ZERO, i}); + g_available_coins.emplace_back(uint256::ZERO, i); } } @@ -45,11 +45,11 @@ FUZZ_TARGET(mini_miner, .init = initialize_miner) const size_t num_outputs = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(1, 50); for (size_t n{0}; n < num_inputs; ++n) { auto prevout = available_coins.front(); - mtx.vin.push_back(CTxIn(prevout, CScript())); + mtx.vin.emplace_back(prevout, CScript()); available_coins.pop_front(); } for (uint32_t n{0}; n < num_outputs; ++n) { - mtx.vout.push_back(CTxOut(100, P2WSH_OP_TRUE)); + mtx.vout.emplace_back(100, P2WSH_OP_TRUE); } CTransactionRef tx = MakeTransactionRef(mtx); TestMemPoolEntryHelper entry; @@ -60,14 +60,14 @@ FUZZ_TARGET(mini_miner, .init = initialize_miner) // All outputs are available to spend for (uint32_t n{0}; n < num_outputs; ++n) { if (fuzzed_data_provider.ConsumeBool()) { - available_coins.push_back(COutPoint{tx->GetHash(), n}); + available_coins.emplace_back(tx->GetHash(), n); } } if (fuzzed_data_provider.ConsumeBool() && !tx->vout.empty()) { // Add outpoint from this tx (may or not be spent by a later tx) - outpoints.push_back(COutPoint{tx->GetHash(), - (uint32_t)fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, tx->vout.size())}); + outpoints.emplace_back(tx->GetHash(), + (uint32_t)fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, tx->vout.size())); } else { // Add some random outpoint (will be interpreted as confirmed or not yet submitted // to mempool). @@ -123,11 +123,11 @@ FUZZ_TARGET(mini_miner_selection, .init = initialize_miner) const size_t num_outputs = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(2, 5); for (size_t n{0}; n < num_inputs; ++n) { auto prevout = available_coins.at(0); - mtx.vin.push_back(CTxIn(prevout, CScript())); + mtx.vin.emplace_back(prevout, CScript()); available_coins.pop_front(); } for (uint32_t n{0}; n < num_outputs; ++n) { - mtx.vout.push_back(CTxOut(100, P2WSH_OP_TRUE)); + mtx.vout.emplace_back(100, P2WSH_OP_TRUE); } CTransactionRef tx = MakeTransactionRef(mtx); @@ -136,9 +136,9 @@ FUZZ_TARGET(mini_miner_selection, .init = initialize_miner) // MiniMiner interprets spent coins as to-be-replaced and excludes them. for (uint32_t n{0}; n < num_outputs - 1; ++n) { if (fuzzed_data_provider.ConsumeBool()) { - available_coins.push_front(COutPoint{tx->GetHash(), n}); + available_coins.emplace_front(tx->GetHash(), n); } else { - available_coins.push_back(COutPoint{tx->GetHash(), n}); + available_coins.emplace_back(tx->GetHash(), n); } } diff --git a/src/test/fuzz/miniscript.cpp b/src/test/fuzz/miniscript.cpp index 0246507da1..8c73edfa9d 100644 --- a/src/test/fuzz/miniscript.cpp +++ b/src/test/fuzz/miniscript.cpp @@ -7,6 +7,7 @@ #include <key.h> #include <script/miniscript.h> #include <script/script.h> +#include <script/signingprovider.h> #include <test/fuzz/FuzzedDataProvider.h> #include <test/fuzz/fuzz.h> #include <test/fuzz/util.h> @@ -14,6 +15,13 @@ namespace { +using Fragment = miniscript::Fragment; +using NodeRef = miniscript::NodeRef<CPubKey>; +using Node = miniscript::Node<CPubKey>; +using Type = miniscript::Type; +using MsCtx = miniscript::MiniscriptContext; +using miniscript::operator"" _mst; + //! Some pre-computed data for more efficient string roundtrips and to simulate challenges. struct TestData { typedef CPubKey Key; @@ -23,6 +31,7 @@ struct TestData { std::map<Key, int> dummy_key_idx_map; std::map<CKeyID, Key> dummy_keys_map; std::map<Key, std::pair<std::vector<unsigned char>, bool>> dummy_sigs; + std::map<XOnlyPubKey, std::pair<std::vector<unsigned char>, bool>> schnorr_sigs; // Precomputed hashes of each kind. std::vector<std::vector<unsigned char>> sha256; @@ -37,6 +46,11 @@ struct TestData { //! Set the precomputed data. 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")}; + // We don't pass additional randomness when creating a schnorr signature. + auto const EMPTY_AUX{uint256S("")}; + for (size_t i = 0; i < 256; i++) { keydata[31] = i; CKey privkey; @@ -46,11 +60,18 @@ struct TestData { dummy_keys.push_back(pubkey); dummy_key_idx_map.emplace(pubkey, i); dummy_keys_map.insert({pubkey.GetID(), pubkey}); + XOnlyPubKey xonly_pubkey{pubkey}; + dummy_key_idx_map.emplace(xonly_pubkey, i); + uint160 xonly_hash{Hash160(xonly_pubkey)}; + dummy_keys_map.emplace(xonly_hash, pubkey); - std::vector<unsigned char> sig; - privkey.Sign(uint256S(""), sig); + std::vector<unsigned char> sig, schnorr_sig(64); + privkey.Sign(MESSAGE_HASH, sig); sig.push_back(1); // SIGHASH_ALL dummy_sigs.insert({pubkey, {sig, i & 1}}); + assert(privkey.SignSchnorr(MESSAGE_HASH, schnorr_sig, nullptr, EMPTY_AUX)); + schnorr_sig.push_back(1); // Maximally-sized signature has sighash byte + schnorr_sigs.emplace(XOnlyPubKey{pubkey}, std::make_pair(std::move(schnorr_sig), i & 1)); std::vector<unsigned char> hash; hash.resize(32); @@ -70,6 +91,19 @@ struct TestData { if (i & 1) hash160_preimages[hash] = std::vector<unsigned char>(keydata, keydata + 32); } } + + //! Get the (Schnorr or ECDSA, depending on context) signature for this pubkey. + const std::pair<std::vector<unsigned char>, bool>* GetSig(const MsCtx script_ctx, const Key& key) const { + if (!miniscript::IsTapscript(script_ctx)) { + const auto it = dummy_sigs.find(key); + if (it == dummy_sigs.end()) return nullptr; + return &it->second; + } else { + const auto it = schnorr_sigs.find(XOnlyPubKey{key}); + if (it == schnorr_sigs.end()) return nullptr; + return &it->second; + } + } } TEST_DATA; /** @@ -80,6 +114,10 @@ struct TestData { struct ParserContext { typedef CPubKey Key; + const MsCtx script_ctx; + + constexpr ParserContext(MsCtx ctx) noexcept : script_ctx(ctx) {} + bool KeyCompare(const Key& a, const Key& b) const { return a < b; } @@ -92,14 +130,20 @@ struct ParserContext { return HexStr(Span{&idx, 1}); } - std::vector<unsigned char> ToPKBytes(const Key& key) const - { - return {key.begin(), key.end()}; + std::vector<unsigned char> ToPKBytes(const Key& key) const { + if (!miniscript::IsTapscript(script_ctx)) { + return {key.begin(), key.end()}; + } + const XOnlyPubKey xonly_pubkey{key}; + return {xonly_pubkey.begin(), xonly_pubkey.end()}; } - std::vector<unsigned char> ToPKHBytes(const Key& key) const - { - const auto h = Hash160(key); + std::vector<unsigned char> ToPKHBytes(const Key& key) const { + if (!miniscript::IsTapscript(script_ctx)) { + const auto h = Hash160(key); + return {h.begin(), h.end()}; + } + const auto h = Hash160(XOnlyPubKey{key}); return {h.begin(), h.end()}; } @@ -113,10 +157,15 @@ struct ParserContext { template<typename I> std::optional<Key> FromPKBytes(I first, I last) const { - CPubKey key; - key.Set(first, last); - if (!key.IsValid()) return {}; - return key; + if (!miniscript::IsTapscript(script_ctx)) { + Key key{first, last}; + if (key.IsValid()) return key; + return {}; + } + if (last - first != 32) return {}; + XOnlyPubKey xonly_pubkey; + std::copy(first, last, xonly_pubkey.begin()); + return xonly_pubkey.GetEvenCorrespondingCPubKey(); } template<typename I> @@ -128,10 +177,18 @@ struct ParserContext { if (it == TEST_DATA.dummy_keys_map.end()) return {}; return it->second; } -} PARSER_CTX; + + MsCtx MsContext() const { + return script_ctx; + } +}; //! Context that implements naive conversion from/to script only, for roundtrip testing. struct ScriptParserContext { + const MsCtx script_ctx; + + constexpr ScriptParserContext(MsCtx ctx) noexcept : script_ctx(ctx) {} + //! For Script roundtrip we never need the key from a key hash. struct Key { bool is_hash; @@ -172,10 +229,17 @@ struct ScriptParserContext { key.is_hash = true; return key; } -} SCRIPT_PARSER_CONTEXT; + + MsCtx MsContext() const { + return script_ctx; + } +}; //! Context to produce a satisfaction for a Miniscript node using the pre-computed data. -struct SatisfierContext: ParserContext { +struct SatisfierContext : ParserContext { + + constexpr SatisfierContext(MsCtx ctx) noexcept : ParserContext(ctx) {} + // Timelock challenges satisfaction. Make the value (deterministically) vary to explore different // paths. bool CheckAfter(uint32_t value) const { return value % 2; } @@ -183,15 +247,11 @@ struct SatisfierContext: ParserContext { // Signature challenges fulfilled with a dummy signature, if it was one of our dummy keys. miniscript::Availability Sign(const CPubKey& key, std::vector<unsigned char>& sig) const { - const auto it = TEST_DATA.dummy_sigs.find(key); - if (it == TEST_DATA.dummy_sigs.end()) return miniscript::Availability::NO; - if (it->second.second) { - // Key is "available" - sig = it->second.first; - return miniscript::Availability::YES; - } else { - return miniscript::Availability::NO; + bool sig_available{false}; + if (auto res = TEST_DATA.GetSig(script_ctx, key)) { + std::tie(sig, sig_available) = *res; } + return sig_available ? miniscript::Availability::YES : miniscript::Availability::NO; } //! Lookup generalization for all the hash satisfactions below @@ -215,12 +275,10 @@ struct SatisfierContext: ParserContext { miniscript::Availability SatHASH160(const std::vector<unsigned char>& hash, std::vector<unsigned char>& preimage) const { return LookupHash(hash, preimage, TEST_DATA.hash160_preimages); } -} SATISFIER_CTX; +}; //! Context to check a satisfaction against the pre-computed data. -struct CheckerContext: BaseSignatureChecker { - TestData *test_data; - +const struct CheckerContext: BaseSignatureChecker { // Signature checker methods. Checks the right dummy signature is used. bool CheckECDSASignature(const std::vector<unsigned char>& sig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override @@ -230,12 +288,19 @@ struct CheckerContext: BaseSignatureChecker { if (it == TEST_DATA.dummy_sigs.end()) return false; return it->second.first == sig; } + bool CheckSchnorrSignature(Span<const unsigned char> sig, Span<const unsigned char> pubkey, SigVersion, + ScriptExecutionData&, ScriptError*) const override { + XOnlyPubKey pk{pubkey}; + auto it = TEST_DATA.schnorr_sigs.find(pk); + if (it == TEST_DATA.schnorr_sigs.end()) return false; + return it->second.first == sig; + } bool CheckLockTime(const CScriptNum& nLockTime) const override { return nLockTime.GetInt64() & 1; } bool CheckSequence(const CScriptNum& nSequence) const override { return nSequence.GetInt64() & 1; } } CHECKER_CTX; //! Context to check for duplicates when instancing a Node. -struct KeyComparator { +const struct KeyComparator { bool KeyCompare(const CPubKey& a, const CPubKey& b) const { return a < b; } @@ -244,11 +309,8 @@ struct KeyComparator { // A dummy scriptsig to pass to VerifyScript (we always use Segwit v0). const CScript DUMMY_SCRIPTSIG; -using Fragment = miniscript::Fragment; -using NodeRef = miniscript::NodeRef<CPubKey>; -using Node = miniscript::Node<CPubKey>; -using Type = miniscript::Type; -using miniscript::operator"" _mst; +//! Public key to be used as internal key for dummy Taproot spends. +const std::vector<unsigned char> NUMS_PK{ParseHex("50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0")}; //! Construct a miniscript node as a shared_ptr. template<typename... Args> NodeRef MakeNodeRef(Args&&... args) { @@ -321,9 +383,10 @@ std::optional<uint32_t> ConsumeTimeLock(FuzzedDataProvider& provider) { * - For pk_k(), pk_h(), and all hashes, the next byte defines the index of the value in the test data. * - For multi(), the next 2 bytes define respectively the threshold and the number of keys. Then as many * bytes as the number of keys define the index of each key in the test data. + * - For multi_a(), same as for multi() but the threshold and the keys count are encoded on two bytes. * - For thresh(), the next byte defines the threshold value and the following one the number of subs. */ -std::optional<NodeInfo> ConsumeNodeStable(FuzzedDataProvider& provider, Type type_needed) { +std::optional<NodeInfo> ConsumeNodeStable(MsCtx script_ctx, FuzzedDataProvider& provider, Type type_needed) { bool allow_B = (type_needed == ""_mst) || (type_needed << "B"_mst); bool allow_K = (type_needed == ""_mst) || (type_needed << "K"_mst); bool allow_V = (type_needed == ""_mst) || (type_needed << "V"_mst); @@ -367,7 +430,7 @@ std::optional<NodeInfo> ConsumeNodeStable(FuzzedDataProvider& provider, Type typ if (!allow_B) return {}; return {{Fragment::HASH160, ConsumeHash160(provider)}}; case 10: { - if (!allow_B) return {}; + if (!allow_B || IsTapscript(script_ctx)) return {}; const auto k = provider.ConsumeIntegral<uint8_t>(); const auto n_keys = provider.ConsumeIntegral<uint8_t>(); if (n_keys > 20 || k == 0 || k > n_keys) return {}; @@ -428,6 +491,15 @@ std::optional<NodeInfo> ConsumeNodeStable(FuzzedDataProvider& provider, Type typ case 26: if (!allow_B) return {}; return {{{"B"_mst}, Fragment::WRAP_N}}; + case 27: { + if (!allow_B || !IsTapscript(script_ctx)) return {}; + const auto k = provider.ConsumeIntegral<uint16_t>(); + const auto n_keys = provider.ConsumeIntegral<uint16_t>(); + if (n_keys > 999 || k == 0 || k > n_keys) return {}; + std::vector<CPubKey> keys{n_keys}; + for (auto& key: keys) key = ConsumePubKey(provider); + return {{Fragment::MULTI_A, k, std::move(keys)}}; + } default: break; } @@ -444,10 +516,16 @@ std::optional<NodeInfo> ConsumeNodeStable(FuzzedDataProvider& provider, Type typ struct SmartInfo { using recipe = std::pair<Fragment, std::vector<Type>>; - std::map<Type, std::vector<recipe>> table; + std::map<Type, std::vector<recipe>> wsh_table, tap_table; void Init() { + Init(wsh_table, MsCtx::P2WSH); + Init(tap_table, MsCtx::TAPSCRIPT); + } + + void Init(std::map<Type, std::vector<recipe>>& table, MsCtx script_ctx) + { /* Construct a set of interesting type requirements to reason with (sections of BKVWzondu). */ std::vector<Type> types; for (int base = 0; base < 4; ++base) { /* select from B,K,V,W */ @@ -495,7 +573,7 @@ struct SmartInfo std::sort(types.begin(), types.end()); // Iterate over all possible fragments. - for (int fragidx = 0; fragidx <= int(Fragment::MULTI); ++fragidx) { + for (int fragidx = 0; fragidx <= int(Fragment::MULTI_A); ++fragidx) { int sub_count = 0; //!< The minimum number of child nodes this recipe has. int sub_range = 1; //!< The maximum number of child nodes for this recipe is sub_count+sub_range-1. size_t data_size = 0; @@ -503,6 +581,12 @@ struct SmartInfo uint32_t k = 0; Fragment frag{fragidx}; + // Only produce recipes valid in the given context. + if ((!miniscript::IsTapscript(script_ctx) && frag == Fragment::MULTI_A) + || (miniscript::IsTapscript(script_ctx) && frag == Fragment::MULTI)) { + continue; + } + // Based on the fragment, determine #subs/data/k/keys to pass to ComputeType. */ switch (frag) { case Fragment::PK_K: @@ -510,6 +594,7 @@ struct SmartInfo n_keys = 1; break; case Fragment::MULTI: + case Fragment::MULTI_A: n_keys = 1; k = 1; break; @@ -568,7 +653,7 @@ struct SmartInfo if (subs > 0) subt.push_back(x); if (subs > 1) subt.push_back(y); if (subs > 2) subt.push_back(z); - Type res = miniscript::internal::ComputeType(frag, x, y, z, subt, k, data_size, subs, n_keys); + Type res = miniscript::internal::ComputeType(frag, x, y, z, subt, k, data_size, subs, n_keys, script_ctx); // Continue if the result is not a valid node. if ((res << "K"_mst) + (res << "V"_mst) + (res << "B"_mst) + (res << "W"_mst) != 1) continue; @@ -685,10 +770,11 @@ struct SmartInfo * (as improvements to the tables or changes to the typing rules could invalidate * everything). */ -std::optional<NodeInfo> ConsumeNodeSmart(FuzzedDataProvider& provider, Type type_needed) { +std::optional<NodeInfo> ConsumeNodeSmart(MsCtx script_ctx, FuzzedDataProvider& provider, Type type_needed) { /** Table entry for the requested type. */ - auto recipes_it = SMARTINFO.table.find(type_needed); - assert(recipes_it != SMARTINFO.table.end()); + const auto& table{IsTapscript(script_ctx) ? SMARTINFO.tap_table : SMARTINFO.wsh_table}; + auto recipes_it = table.find(type_needed); + assert(recipes_it != table.end()); /** Pick one recipe from the available ones for that type. */ const auto& [frag, subt] = PickValue(provider, recipes_it->second); @@ -704,6 +790,13 @@ std::optional<NodeInfo> ConsumeNodeSmart(FuzzedDataProvider& provider, Type type for (auto& key: keys) key = ConsumePubKey(provider); return {{frag, k, std::move(keys)}}; } + case Fragment::MULTI_A: { + const auto n_keys = provider.ConsumeIntegralInRange<uint16_t>(1, 999); + const auto k = provider.ConsumeIntegralInRange<uint16_t>(1, n_keys); + std::vector<CPubKey> keys{n_keys}; + for (auto& key: keys) key = ConsumePubKey(provider); + return {{frag, k, std::move(keys)}}; + } case Fragment::OLDER: case Fragment::AFTER: return {{frag, provider.ConsumeIntegralInRange<uint32_t>(1, 0x7FFFFFF)}}; @@ -760,7 +853,7 @@ std::optional<NodeInfo> ConsumeNodeSmart(FuzzedDataProvider& provider, Type type * a NodeRef whose Type() matches the type fed to ConsumeNode. */ template<typename F> -NodeRef GenNode(F ConsumeNode, Type root_type, bool strict_valid = false) { +NodeRef GenNode(MsCtx script_ctx, F ConsumeNode, Type root_type, bool strict_valid = false) { /** A stack of miniscript Nodes being built up. */ std::vector<NodeRef> stack; /** The queue of instructions. */ @@ -781,7 +874,8 @@ NodeRef GenNode(F ConsumeNode, Type root_type, bool strict_valid = false) { // Update predicted resource limits. Since every leaf Miniscript node is at least one // byte long, we move one byte from each child to their parent. A similar technique is // used in the miniscript::internal::Parse function to prevent runaway string parsing. - scriptsize += miniscript::internal::ComputeScriptLen(node_info->fragment, ""_mst, node_info->subtypes.size(), node_info->k, node_info->subtypes.size(), node_info->keys.size()) - 1; + scriptsize += miniscript::internal::ComputeScriptLen(node_info->fragment, ""_mst, node_info->subtypes.size(), node_info->k, node_info->subtypes.size(), + node_info->keys.size(), script_ctx) - 1; if (scriptsize > MAX_STANDARD_P2WSH_SCRIPT_SIZE) return {}; switch (node_info->fragment) { case Fragment::JUST_0: @@ -826,6 +920,9 @@ NodeRef GenNode(F ConsumeNode, Type root_type, bool strict_valid = false) { case Fragment::MULTI: ops += 1; break; + case Fragment::MULTI_A: + ops += node_info->keys.size() + 1; + break; case Fragment::WRAP_A: ops += 2; break; @@ -874,11 +971,11 @@ NodeRef GenNode(F ConsumeNode, Type root_type, bool strict_valid = false) { // Construct new NodeRef. NodeRef node; if (info.keys.empty()) { - node = MakeNodeRef(info.fragment, std::move(sub), std::move(info.hash), info.k); + node = MakeNodeRef(script_ctx, info.fragment, std::move(sub), std::move(info.hash), info.k); } else { assert(sub.empty()); assert(info.hash.empty()); - node = MakeNodeRef(info.fragment, std::move(info.keys), info.k); + node = MakeNodeRef(script_ctx, info.fragment, std::move(info.keys), info.k); } // Verify acceptability. if (!node || (node->GetType() & "KVWB"_mst) == ""_mst) { @@ -894,8 +991,10 @@ NodeRef GenNode(F ConsumeNode, Type root_type, bool strict_valid = false) { ops += 1; scriptsize += 1; } - if (ops > MAX_OPS_PER_SCRIPT) return {}; - if (scriptsize > MAX_STANDARD_P2WSH_SCRIPT_SIZE) return {}; + if (!miniscript::IsTapscript(script_ctx) && ops > MAX_OPS_PER_SCRIPT) return {}; + if (scriptsize > miniscript::internal::MaxScriptSize(script_ctx)) { + return {}; + } // Move it to the stack. stack.push_back(std::move(node)); todo.pop_back(); @@ -908,44 +1007,67 @@ NodeRef GenNode(F ConsumeNode, Type root_type, bool strict_valid = false) { return std::move(stack[0]); } +//! The spk for this script under the given context. If it's a Taproot output also record the spend data. +CScript ScriptPubKey(MsCtx ctx, const CScript& script, TaprootBuilder& builder) +{ + if (!miniscript::IsTapscript(ctx)) return CScript() << OP_0 << WitnessV0ScriptHash(script); + + // For Taproot outputs we always use a tree with a single script and a dummy internal key. + builder.Add(0, script, TAPROOT_LEAF_TAPSCRIPT); + builder.Finalize(XOnlyPubKey{NUMS_PK}); + return GetScriptForDestination(builder.GetOutput()); +} + +//! Fill the witness with the data additional to the script satisfaction. +void SatisfactionToWitness(MsCtx ctx, CScriptWitness& witness, const CScript& script, TaprootBuilder& builder) { + // For P2WSH, it's only the witness script. + witness.stack.emplace_back(script.begin(), script.end()); + if (!miniscript::IsTapscript(ctx)) return; + // For Tapscript we also need the control block. + witness.stack.push_back(*builder.GetSpendData().scripts.begin()->second.begin()); +} + /** Perform various applicable tests on a miniscript Node. */ -void TestNode(const NodeRef& node, FuzzedDataProvider& provider) +void TestNode(const MsCtx script_ctx, const NodeRef& node, FuzzedDataProvider& provider) { if (!node) return; // Check that it roundtrips to text representation - std::optional<std::string> str{node->ToString(PARSER_CTX)}; + const ParserContext parser_ctx{script_ctx}; + std::optional<std::string> str{node->ToString(parser_ctx)}; assert(str); - auto parsed = miniscript::FromString(*str, PARSER_CTX); + auto parsed = miniscript::FromString(*str, parser_ctx); assert(parsed); assert(*parsed == *node); // Check consistency between script size estimation and real size. - auto script = node->ToScript(PARSER_CTX); + auto script = node->ToScript(parser_ctx); assert(node->ScriptSize() == script.size()); // Check consistency of "x" property with the script (type K is excluded, because it can end // with a push of a key, which could match these opcodes). if (!(node->GetType() << "K"_mst)) { bool ends_in_verify = !(node->GetType() << "x"_mst); - assert(ends_in_verify == (script.back() == OP_CHECKSIG || script.back() == OP_CHECKMULTISIG || script.back() == OP_EQUAL)); + assert(ends_in_verify == (script.back() == OP_CHECKSIG || script.back() == OP_CHECKMULTISIG || script.back() == OP_EQUAL || script.back() == OP_NUMEQUAL)); } // The rest of the checks only apply when testing a valid top-level script. if (!node->IsValidTopLevel()) return; // Check roundtrip to script - auto decoded = miniscript::FromScript(script, PARSER_CTX); + auto decoded = miniscript::FromScript(script, parser_ctx); assert(decoded); // Note we can't use *decoded == *node because the miniscript representation may differ, so we check that: // - The script corresponding to that decoded form matches exactly // - The type matches exactly - assert(decoded->ToScript(PARSER_CTX) == script); + assert(decoded->ToScript(parser_ctx) == script); assert(decoded->GetType() == node->GetType()); - const auto node_ops{node->GetOps()}; - if (provider.ConsumeBool() && node_ops && *node_ops < MAX_OPS_PER_SCRIPT && node->ScriptSize() < MAX_STANDARD_P2WSH_SCRIPT_SIZE) { - // Optionally pad the script with OP_NOPs to max op the ops limit of the constructed script. + // Optionally pad the script or the witness in order to increase the sensitivity of the tests of + // the resources limits logic. + CScriptWitness witness_mal, witness_nonmal; + if (provider.ConsumeBool()) { + // Under P2WSH, optionally pad the script with OP_NOPs to max op the ops limit of the constructed script. // This makes the script obviously not actually miniscript-compatible anymore, but the // signatures constructed in this test don't commit to the script anyway, so the same // miniscript satisfier will work. This increases the sensitivity of the test to the ops @@ -954,31 +1076,57 @@ void TestNode(const NodeRef& node, FuzzedDataProvider& provider) // maximal. // Do not pad more than what would cause MAX_STANDARD_P2WSH_SCRIPT_SIZE to be reached, however, // as that also invalidates scripts. - int add = std::min<int>( - MAX_OPS_PER_SCRIPT - *node_ops, - MAX_STANDARD_P2WSH_SCRIPT_SIZE - node->ScriptSize()); - for (int i = 0; i < add; ++i) script.push_back(OP_NOP); + const auto node_ops{node->GetOps()}; + if (!IsTapscript(script_ctx) && node_ops && *node_ops < MAX_OPS_PER_SCRIPT + && node->ScriptSize() < MAX_STANDARD_P2WSH_SCRIPT_SIZE) { + int add = std::min<int>( + MAX_OPS_PER_SCRIPT - *node_ops, + MAX_STANDARD_P2WSH_SCRIPT_SIZE - node->ScriptSize()); + for (int i = 0; i < add; ++i) script.push_back(OP_NOP); + } + + // Under Tapscript, optionally pad the stack up to the limit minus the calculated maximum execution stack + // size to assert a Miniscript would never add more elements to the stack during execution than anticipated. + const auto node_exec_ss{node->GetExecStackSize()}; + if (miniscript::IsTapscript(script_ctx) && node_exec_ss && *node_exec_ss < MAX_STACK_SIZE) { + unsigned add{(unsigned)MAX_STACK_SIZE - *node_exec_ss}; + witness_mal.stack.resize(add); + witness_nonmal.stack.resize(add); + script.reserve(add); + for (unsigned i = 0; i < add; ++i) script.push_back(OP_NIP); + } } + const SatisfierContext satisfier_ctx{script_ctx}; + + // Get the ScriptPubKey for this script, filling spend data if it's Taproot. + TaprootBuilder builder; + const CScript script_pubkey{ScriptPubKey(script_ctx, script, builder)}; + // Run malleable satisfaction algorithm. - const CScript script_pubkey = CScript() << OP_0 << WitnessV0ScriptHash(script); - CScriptWitness witness_mal; - const bool mal_success = node->Satisfy(SATISFIER_CTX, witness_mal.stack, false) == miniscript::Availability::YES; - witness_mal.stack.push_back(std::vector<unsigned char>(script.begin(), script.end())); + std::vector<std::vector<unsigned char>> stack_mal; + const bool mal_success = node->Satisfy(satisfier_ctx, stack_mal, false) == miniscript::Availability::YES; // Run non-malleable satisfaction algorithm. - CScriptWitness witness_nonmal; - const bool nonmal_success = node->Satisfy(SATISFIER_CTX, witness_nonmal.stack, true) == miniscript::Availability::YES; - witness_nonmal.stack.push_back(std::vector<unsigned char>(script.begin(), script.end())); + std::vector<std::vector<unsigned char>> stack_nonmal; + const bool nonmal_success = node->Satisfy(satisfier_ctx, stack_nonmal, true) == miniscript::Availability::YES; if (nonmal_success) { - // Non-malleable satisfactions are bounded by GetStackSize(). - assert(witness_nonmal.stack.size() <= *node->GetStackSize() + 1); + // Non-malleable satisfactions are bounded by the satisfaction size plus: + // - For P2WSH spends, the witness script + // - For Tapscript spends, both the witness script and the control block + const size_t max_stack_size{*node->GetStackSize() + 1 + miniscript::IsTapscript(script_ctx)}; + assert(stack_nonmal.size() <= max_stack_size); // If a non-malleable satisfaction exists, the malleable one must also exist, and be identical to it. assert(mal_success); - assert(witness_nonmal.stack == witness_mal.stack); + assert(stack_nonmal == stack_mal); + // Compute witness size (excluding script push, control block, and witness count encoding). + const size_t wit_size = GetSerializeSize(stack_nonmal, PROTOCOL_VERSION) - GetSizeOfCompactSize(stack_nonmal.size()); + assert(wit_size <= *node->GetWitnessSize()); // Test non-malleable satisfaction. + witness_nonmal.stack.insert(witness_nonmal.stack.end(), std::make_move_iterator(stack_nonmal.begin()), std::make_move_iterator(stack_nonmal.end())); + SatisfactionToWitness(script_ctx, witness_nonmal, script, builder); ScriptError serror; bool res = VerifyScript(DUMMY_SCRIPTSIG, script_pubkey, &witness_nonmal, STANDARD_SCRIPT_VERIFY_FLAGS, CHECKER_CTX, &serror); // Non-malleable satisfactions are guaranteed to be valid if ValidSatisfactions(). @@ -992,6 +1140,8 @@ void TestNode(const NodeRef& node, FuzzedDataProvider& provider) if (mal_success && (!nonmal_success || witness_mal.stack != witness_nonmal.stack)) { // Test malleable satisfaction only if it's different from the non-malleable one. + witness_mal.stack.insert(witness_mal.stack.end(), std::make_move_iterator(stack_mal.begin()), std::make_move_iterator(stack_mal.end())); + SatisfactionToWitness(script_ctx, witness_mal, script, builder); ScriptError serror; bool res = VerifyScript(DUMMY_SCRIPTSIG, script_pubkey, &witness_mal, STANDARD_SCRIPT_VERIFY_FLAGS, CHECKER_CTX, &serror); // Malleable satisfactions are not guaranteed to be valid under any conditions, but they can only @@ -1008,21 +1158,20 @@ void TestNode(const NodeRef& node, FuzzedDataProvider& provider) // algorithm succeeds. Given that under IsSane() both satisfactions // are identical, this implies that for such nodes, the non-malleable // satisfaction will also match the expected policy. - bool satisfiable = node->IsSatisfiable([](const Node& node) -> bool { + const auto is_key_satisfiable = [script_ctx](const CPubKey& pubkey) -> bool { + auto sig_ptr{TEST_DATA.GetSig(script_ctx, pubkey)}; + return sig_ptr != nullptr && sig_ptr->second; + }; + bool satisfiable = node->IsSatisfiable([&](const Node& node) -> bool { switch (node.fragment) { case Fragment::PK_K: - case Fragment::PK_H: { - auto it = TEST_DATA.dummy_sigs.find(node.keys[0]); - assert(it != TEST_DATA.dummy_sigs.end()); - return it->second.second; - } - case Fragment::MULTI: { - size_t sats = 0; - for (const auto& key : node.keys) { - auto it = TEST_DATA.dummy_sigs.find(key); - assert(it != TEST_DATA.dummy_sigs.end()); - sats += it->second.second; - } + case Fragment::PK_H: + return is_key_satisfiable(node.keys[0]); + case Fragment::MULTI: + case Fragment::MULTI_A: { + size_t sats = std::count_if(node.keys.begin(), node.keys.end(), [&](const auto& key) { + return size_t(is_key_satisfiable(key)); + }); return sats >= node.k; } case Fragment::OLDER: @@ -1061,10 +1210,13 @@ void FuzzInitSmart() /** Fuzz target that runs TestNode on nodes generated using ConsumeNodeStable. */ FUZZ_TARGET(miniscript_stable, .init = FuzzInit) { - FuzzedDataProvider provider(buffer.data(), buffer.size()); - TestNode(GenNode([&](Type needed_type) { - return ConsumeNodeStable(provider, needed_type); - }, ""_mst), provider); + // Run it under both P2WSH and Tapscript contexts. + for (const auto script_ctx: {MsCtx::P2WSH, MsCtx::TAPSCRIPT}) { + FuzzedDataProvider provider(buffer.data(), buffer.size()); + TestNode(script_ctx, GenNode(script_ctx, [&](Type needed_type) { + return ConsumeNodeStable(script_ctx, provider, needed_type); + }, ""_mst), provider); + } } /** Fuzz target that runs TestNode on nodes generated using ConsumeNodeSmart. */ @@ -1074,22 +1226,25 @@ FUZZ_TARGET(miniscript_smart, .init = FuzzInitSmart) static constexpr std::array<Type, 4> BASE_TYPES{"B"_mst, "V"_mst, "K"_mst, "W"_mst}; FuzzedDataProvider provider(buffer.data(), buffer.size()); - TestNode(GenNode([&](Type needed_type) { - return ConsumeNodeSmart(provider, needed_type); + const auto script_ctx{(MsCtx)provider.ConsumeBool()}; + TestNode(script_ctx, GenNode(script_ctx, [&](Type needed_type) { + return ConsumeNodeSmart(script_ctx, provider, needed_type); }, PickValue(provider, BASE_TYPES), true), provider); } /* Fuzz tests that test parsing from a string, and roundtripping via string. */ FUZZ_TARGET(miniscript_string, .init = FuzzInit) { + if (buffer.empty()) return; FuzzedDataProvider provider(buffer.data(), buffer.size()); - auto str = provider.ConsumeRemainingBytesAsString(); - auto parsed = miniscript::FromString(str, PARSER_CTX); + auto str = provider.ConsumeBytesAsString(provider.remaining_bytes() - 1); + const ParserContext parser_ctx{(MsCtx)provider.ConsumeBool()}; + auto parsed = miniscript::FromString(str, parser_ctx); if (!parsed) return; - const auto str2 = parsed->ToString(PARSER_CTX); + const auto str2 = parsed->ToString(parser_ctx); assert(str2); - auto parsed2 = miniscript::FromString(*str2, PARSER_CTX); + auto parsed2 = miniscript::FromString(*str2, parser_ctx); assert(parsed2); assert(*parsed == *parsed2); } @@ -1101,8 +1256,9 @@ FUZZ_TARGET(miniscript_script) const std::optional<CScript> script = ConsumeDeserializable<CScript>(fuzzed_data_provider); if (!script) return; - const auto ms = miniscript::FromScript(*script, SCRIPT_PARSER_CONTEXT); + const ScriptParserContext script_parser_ctx{(MsCtx)fuzzed_data_provider.ConsumeBool()}; + const auto ms = miniscript::FromScript(*script, script_parser_ctx); if (!ms) return; - assert(ms->ToScript(SCRIPT_PARSER_CONTEXT) == *script); + assert(ms->ToScript(script_parser_ctx) == *script); } diff --git a/src/test/fuzz/script_bitcoin_consensus.cpp b/src/test/fuzz/script_bitcoin_consensus.cpp index fcd66b234e..846389863d 100644 --- a/src/test/fuzz/script_bitcoin_consensus.cpp +++ b/src/test/fuzz/script_bitcoin_consensus.cpp @@ -28,4 +28,23 @@ FUZZ_TARGET(script_bitcoin_consensus) } (void)bitcoinconsensus_verify_script(random_bytes_1.data(), random_bytes_1.size(), random_bytes_2.data(), random_bytes_2.size(), n_in, flags, err_p); (void)bitcoinconsensus_verify_script_with_amount(random_bytes_1.data(), random_bytes_1.size(), money, random_bytes_2.data(), random_bytes_2.size(), n_in, flags, err_p); + + std::vector<UTXO> spent_outputs; + std::vector<std::vector<unsigned char>> spent_spks; + if (n_in <= 24386) { + spent_outputs.reserve(n_in); + spent_spks.reserve(n_in); + for (size_t i = 0; i < n_in; ++i) { + spent_spks.push_back(ConsumeRandomLengthByteVector(fuzzed_data_provider)); + const CAmount value{ConsumeMoney(fuzzed_data_provider)}; + const auto spk_size{static_cast<unsigned>(spent_spks.back().size())}; + spent_outputs.push_back({.scriptPubKey = spent_spks.back().data(), .scriptPubKeySize = spk_size, .value = value}); + } + } + + const auto spent_outs_size{static_cast<unsigned>(spent_outputs.size())}; + + (void)bitcoinconsensus_verify_script_with_spent_outputs( + random_bytes_1.data(), random_bytes_1.size(), money, random_bytes_2.data(), random_bytes_2.size(), + spent_outputs.data(), spent_outs_size, n_in, flags, err_p); } diff --git a/src/test/fuzz/util/net.cpp b/src/test/fuzz/util/net.cpp index d23e997719..5a286c05d2 100644 --- a/src/test/fuzz/util/net.cpp +++ b/src/test/fuzz/util/net.cpp @@ -36,7 +36,11 @@ CNetAddr ConsumeNetAddr(FuzzedDataProvider& fuzzed_data_provider) noexcept } else if (network == Network::NET_IPV6) { if (fuzzed_data_provider.remaining_bytes() >= 16) { in6_addr v6_addr = {}; - memcpy(v6_addr.s6_addr, fuzzed_data_provider.ConsumeBytes<uint8_t>(16).data(), 16); + auto addr_bytes = fuzzed_data_provider.ConsumeBytes<uint8_t>(16); + if (addr_bytes[0] == CJDNS_PREFIX) { // Avoid generating IPv6 addresses that look like CJDNS. + addr_bytes[0] = 0x55; // Just an arbitrary number, anything != CJDNS_PREFIX would do. + } + memcpy(v6_addr.s6_addr, addr_bytes.data(), 16); net_addr = CNetAddr{v6_addr, fuzzed_data_provider.ConsumeIntegral<uint32_t>()}; } } else if (network == Network::NET_INTERNAL) { diff --git a/src/test/headers_sync_chainwork_tests.cpp b/src/test/headers_sync_chainwork_tests.cpp index 41241ebee2..b710ad1801 100644 --- a/src/test/headers_sync_chainwork_tests.cpp +++ b/src/test/headers_sync_chainwork_tests.cpp @@ -40,7 +40,7 @@ void HeadersGeneratorSetup::GenerateHeaders(std::vector<CBlockHeader>& headers, uint256 prev_hash = starting_hash; while (headers.size() < count) { - headers.push_back(CBlockHeader()); + headers.emplace_back(); CBlockHeader& next_header = headers.back();; next_header.nVersion = nVersion; next_header.hashPrevBlock = prev_hash; diff --git a/src/test/miniscript_tests.cpp b/src/test/miniscript_tests.cpp index b69317c4d9..996c379962 100644 --- a/src/test/miniscript_tests.cpp +++ b/src/test/miniscript_tests.cpp @@ -20,6 +20,7 @@ #include <script/interpreter.h> #include <script/miniscript.h> #include <script/script_error.h> +#include <script/signingprovider.h> namespace { @@ -30,7 +31,9 @@ struct TestData { //! A map from the public keys to their CKeyIDs (faster than hashing every time). std::map<CPubKey, CKeyID> pkhashes; std::map<CKeyID, CPubKey> pkmap; + std::map<XOnlyPubKey, CKeyID> xonly_pkhashes; std::map<CPubKey, std::vector<unsigned char>> signatures; + std::map<XOnlyPubKey, std::vector<unsigned char>> schnorr_signatures; // Various precomputed hashes std::vector<std::vector<unsigned char>> sha256; @@ -46,6 +49,9 @@ struct TestData { { // All our signatures sign (and are required to sign) this constant message. auto const MESSAGE_HASH = uint256S("f5cd94e18b6fe77dd7aca9e35c2b0c9cbd86356c80a71065"); + // We don't pass additional randomness when creating a schnorr signature. + auto const EMPTY_AUX{uint256S("")}; + // We generate 255 public keys and 255 hashes of each type. for (int i = 1; i <= 255; ++i) { // This 32-byte array functions as both private key data and hash preimage (31 zero bytes plus any nonzero byte). @@ -60,12 +66,19 @@ struct TestData { pubkeys.push_back(pubkey); pkhashes.emplace(pubkey, keyid); pkmap.emplace(keyid, pubkey); + XOnlyPubKey xonly_pubkey{pubkey}; + uint160 xonly_hash{Hash160(xonly_pubkey)}; + xonly_pkhashes.emplace(xonly_pubkey, xonly_hash); + pkmap.emplace(xonly_hash, pubkey); // Compute ECDSA signatures on MESSAGE_HASH with the private keys. - std::vector<unsigned char> sig; + std::vector<unsigned char> sig, schnorr_sig(64); BOOST_CHECK(key.Sign(MESSAGE_HASH, sig)); sig.push_back(1); // sighash byte signatures.emplace(pubkey, sig); + BOOST_CHECK(key.SignSchnorr(MESSAGE_HASH, schnorr_sig, nullptr, EMPTY_AUX)); + schnorr_sig.push_back(1); // Maximally sized Schnorr sigs have a sighash byte. + schnorr_signatures.emplace(XOnlyPubKey{pubkey}, schnorr_sig); // Compute various hashes std::vector<unsigned char> hash; @@ -114,19 +127,32 @@ typedef std::pair<ChallengeType, uint32_t> Challenge; struct KeyConverter { typedef CPubKey Key; + const miniscript::MiniscriptContext m_script_ctx; + + constexpr KeyConverter(miniscript::MiniscriptContext ctx) noexcept : m_script_ctx{ctx} {} + bool KeyCompare(const Key& a, const Key& b) const { return a < b; } //! Convert a public key to bytes. - std::vector<unsigned char> ToPKBytes(const CPubKey& key) const { return {key.begin(), key.end()}; } + std::vector<unsigned char> ToPKBytes(const CPubKey& key) const { + if (!miniscript::IsTapscript(m_script_ctx)) { + return {key.begin(), key.end()}; + } + const XOnlyPubKey xonly_pubkey{key}; + return {xonly_pubkey.begin(), xonly_pubkey.end()}; + } //! Convert a public key to its Hash160 bytes (precomputed). - std::vector<unsigned char> ToPKHBytes(const CPubKey& key) const - { - auto it = g_testdata->pkhashes.find(key); - assert(it != g_testdata->pkhashes.end()); - return {it->second.begin(), it->second.end()}; + std::vector<unsigned char> ToPKHBytes(const CPubKey& key) const { + if (!miniscript::IsTapscript(m_script_ctx)) { + auto hash = g_testdata->pkhashes.at(key); + return {hash.begin(), hash.end()}; + } + const XOnlyPubKey xonly_key{key}; + auto hash = g_testdata->xonly_pkhashes.at(xonly_key); + return {hash.begin(), hash.end()}; } //! Parse a public key from a range of hex characters. @@ -140,9 +166,15 @@ struct KeyConverter { template<typename I> std::optional<Key> FromPKBytes(I first, I last) const { - Key key{first, last}; - if (key.IsValid()) return key; - return {}; + if (!miniscript::IsTapscript(m_script_ctx)) { + Key key{first, last}; + if (key.IsValid()) return key; + return {}; + } + if (last - first != 32) return {}; + XOnlyPubKey xonly_pubkey; + std::copy(first, last, xonly_pubkey.begin()); + return xonly_pubkey.GetEvenCorrespondingCPubKey(); } template<typename I> @@ -150,18 +182,23 @@ struct KeyConverter { assert(last - first == 20); CKeyID keyid; std::copy(first, last, keyid.begin()); - auto it = g_testdata->pkmap.find(keyid); - assert(it != g_testdata->pkmap.end()); - return it->second; + return g_testdata->pkmap.at(keyid); } std::optional<std::string> ToString(const Key& key) const { return HexStr(ToPKBytes(key)); } + + miniscript::MiniscriptContext MsContext() const { + return m_script_ctx; + } }; /** A class that encapsulates all signing/hash revealing operations. */ struct Satisfier : public KeyConverter { + + Satisfier(miniscript::MiniscriptContext ctx) noexcept : KeyConverter{ctx} {} + //! Which keys/timelocks/hash preimages are available. std::set<Challenge> supported; @@ -178,9 +215,15 @@ struct Satisfier : public KeyConverter { //! Produce a signature for the given key. miniscript::Availability Sign(const CPubKey& key, std::vector<unsigned char>& sig) const { if (supported.count(Challenge(ChallengeType::PK, ChallengeNumber(key)))) { - auto it = g_testdata->signatures.find(key); - if (it == g_testdata->signatures.end()) return miniscript::Availability::NO; - sig = it->second; + if (!miniscript::IsTapscript(m_script_ctx)) { + auto it = g_testdata->signatures.find(key); + if (it == g_testdata->signatures.end()) return miniscript::Availability::NO; + sig = it->second; + } else { + auto it = g_testdata->schnorr_signatures.find(XOnlyPubKey{key}); + if (it == g_testdata->schnorr_signatures.end()) return miniscript::Availability::NO; + sig = it->second; + } return miniscript::Availability::YES; } return miniscript::Availability::NO; @@ -226,6 +269,14 @@ public: return sig == it->second; } + bool CheckSchnorrSignature(Span<const unsigned char> sig, Span<const unsigned char> pubkey, SigVersion, + ScriptExecutionData&, ScriptError*) const override { + XOnlyPubKey pk{pubkey}; + auto it = g_testdata->schnorr_signatures.find(pk); + if (it == g_testdata->schnorr_signatures.end()) return false; + return sig == it->second; + } + bool CheckLockTime(const CScriptNum& locktime) const override { // Delegate to Satisfier. return ctx.CheckAfter(locktime.GetInt64()); @@ -237,8 +288,8 @@ public: } }; -//! Singleton instance of KeyConverter. -const KeyConverter CONVERTER{}; +//! Public key to be used as internal key for dummy Taproot spends. +const std::vector<unsigned char> NUMS_PK{ParseHex("50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0")}; using Fragment = miniscript::Fragment; using NodeRef = miniscript::NodeRef<CPubKey>; @@ -271,37 +322,66 @@ std::set<Challenge> FindChallenges(const NodeRef& ref) { return chal; } +//! The spk for this script under the given context. If it's a Taproot output also record the spend data. +CScript ScriptPubKey(miniscript::MiniscriptContext ctx, const CScript& script, TaprootBuilder& builder) +{ + if (!miniscript::IsTapscript(ctx)) return CScript() << OP_0 << WitnessV0ScriptHash(script); + + // For Taproot outputs we always use a tree with a single script and a dummy internal key. + builder.Add(0, script, TAPROOT_LEAF_TAPSCRIPT); + builder.Finalize(XOnlyPubKey{NUMS_PK}); + return GetScriptForDestination(builder.GetOutput()); +} + +//! Fill the witness with the data additional to the script satisfaction. +void SatisfactionToWitness(miniscript::MiniscriptContext ctx, CScriptWitness& witness, const CScript& script, TaprootBuilder& builder) { + // For P2WSH, it's only the witness script. + witness.stack.emplace_back(script.begin(), script.end()); + if (!miniscript::IsTapscript(ctx)) return; + // For Tapscript we also need the control block. + witness.stack.push_back(*builder.GetSpendData().scripts.begin()->second.begin()); +} + /** Run random satisfaction tests. */ -void TestSatisfy(const std::string& testcase, const NodeRef& node) { - auto script = node->ToScript(CONVERTER); +void TestSatisfy(const KeyConverter& converter, const std::string& testcase, const NodeRef& node) { + auto script = node->ToScript(converter); 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); - Satisfier satisfier; + Satisfier satisfier(converter.MsContext()); TestSignatureChecker checker(satisfier); bool prev_mal_success = false, prev_nonmal_success = false; // Go over all challenges involved in this miniscript in random order. for (int add = -1; add < (int)challist.size(); ++add) { if (add >= 0) satisfier.supported.insert(challist[add]); // The first iteration does not add anything + // Get the ScriptPubKey for this script, filling spend data if it's Taproot. + TaprootBuilder builder; + const CScript script_pubkey{ScriptPubKey(converter.MsContext(), script, builder)}; + // Run malleable satisfaction algorithm. - const CScript script_pubkey = CScript() << OP_0 << WitnessV0ScriptHash(script); CScriptWitness witness_mal; const bool mal_success = node->Satisfy(satisfier, witness_mal.stack, false) == miniscript::Availability::YES; - witness_mal.stack.push_back(std::vector<unsigned char>(script.begin(), script.end())); + SatisfactionToWitness(converter.MsContext(), witness_mal, script, builder); // Run non-malleable satisfaction algorithm. CScriptWitness witness_nonmal; const bool nonmal_success = node->Satisfy(satisfier, witness_nonmal.stack, true) == miniscript::Availability::YES; - witness_nonmal.stack.push_back(std::vector<unsigned char>(script.begin(), script.end())); + // Compute witness size (excluding script push, control block, and witness count encoding). + const size_t wit_size = GetSerializeSize(witness_nonmal.stack, PROTOCOL_VERSION) - GetSizeOfCompactSize(witness_nonmal.stack.size()); + SatisfactionToWitness(converter.MsContext(), witness_nonmal, script, builder); if (nonmal_success) { - // Non-malleable satisfactions are bounded by GetStackSize(). - BOOST_CHECK(witness_nonmal.stack.size() <= *node->GetStackSize() + 1); + // Non-malleable satisfactions are bounded by the satisfaction size plus: + // - For P2WSH spends, the witness script + // - For Tapscript spends, both the witness script and the control block + const size_t max_stack_size{*node->GetStackSize() + 1 + miniscript::IsTapscript(converter.MsContext())}; + BOOST_CHECK(witness_nonmal.stack.size() <= max_stack_size); // If a non-malleable satisfaction exists, the malleable one must also exist, and be identical to it. BOOST_CHECK(mal_success); BOOST_CHECK(witness_nonmal.stack == witness_mal.stack); + assert(wit_size <= *node->GetWitnessSize()); // Test non-malleable satisfaction. ScriptError serror; @@ -351,37 +431,66 @@ void TestSatisfy(const std::string& testcase, const NodeRef& node) { } enum TestMode : int { + //! Invalid under any context TESTMODE_INVALID = 0, + //! Valid under any context unless overridden TESTMODE_VALID = 1, TESTMODE_NONMAL = 2, TESTMODE_NEEDSIG = 4, - TESTMODE_TIMELOCKMIX = 8 + TESTMODE_TIMELOCKMIX = 8, + //! Invalid only under P2WSH context + TESTMODE_P2WSH_INVALID = 16, + //! Invalid only under Tapscript context + TESTMODE_TAPSCRIPT_INVALID = 32, }; -void Test(const std::string& ms, const std::string& hexscript, int mode, int opslimit = -1, int stacklimit = -1, std::optional<uint32_t> max_wit_size = std::nullopt) +void Test(const std::string& ms, const std::string& hexscript, int mode, const KeyConverter& converter, + int opslimit = -1, int stacklimit = -1, std::optional<uint32_t> max_wit_size = std::nullopt, + std::optional<uint32_t> stack_exec = {}) { - auto node = miniscript::FromString(ms, CONVERTER); - if (mode == TESTMODE_INVALID) { + auto node = miniscript::FromString(ms, converter); + const bool is_tapscript{miniscript::IsTapscript(converter.MsContext())}; + if (mode == TESTMODE_INVALID || ((mode & TESTMODE_P2WSH_INVALID) && !is_tapscript) || ((mode & TESTMODE_TAPSCRIPT_INVALID) && is_tapscript)) { BOOST_CHECK_MESSAGE(!node || !node->IsValid(), "Unexpectedly valid: " + ms); } else { BOOST_CHECK_MESSAGE(node, "Unparseable: " + ms); BOOST_CHECK_MESSAGE(node->IsValid(), "Invalid: " + ms); BOOST_CHECK_MESSAGE(node->IsValidTopLevel(), "Invalid top level: " + ms); - auto computed_script = node->ToScript(CONVERTER); + auto computed_script = node->ToScript(converter); BOOST_CHECK_MESSAGE(node->ScriptSize() == computed_script.size(), "Script size mismatch: " + ms); if (hexscript != "?") BOOST_CHECK_MESSAGE(HexStr(computed_script) == hexscript, "Script mismatch: " + ms + " (" + HexStr(computed_script) + " vs " + hexscript + ")"); BOOST_CHECK_MESSAGE(node->IsNonMalleable() == !!(mode & TESTMODE_NONMAL), "Malleability mismatch: " + ms); BOOST_CHECK_MESSAGE(node->NeedsSignature() == !!(mode & TESTMODE_NEEDSIG), "Signature necessity mismatch: " + ms); BOOST_CHECK_MESSAGE((node->GetType() << "k"_mst) == !(mode & TESTMODE_TIMELOCKMIX), "Timelock mix mismatch: " + ms); - auto inferred_miniscript = miniscript::FromScript(computed_script, CONVERTER); + auto inferred_miniscript = miniscript::FromScript(computed_script, converter); BOOST_CHECK_MESSAGE(inferred_miniscript, "Cannot infer miniscript from script: " + ms); - BOOST_CHECK_MESSAGE(inferred_miniscript->ToScript(CONVERTER) == computed_script, "Roundtrip failure: miniscript->script != miniscript->script->miniscript->script: " + ms); + BOOST_CHECK_MESSAGE(inferred_miniscript->ToScript(converter) == computed_script, "Roundtrip failure: miniscript->script != miniscript->script->miniscript->script: " + ms); if (opslimit != -1) BOOST_CHECK_MESSAGE((int)*node->GetOps() == opslimit, "Ops limit mismatch: " << ms << " (" << *node->GetOps() << " vs " << opslimit << ")"); if (stacklimit != -1) BOOST_CHECK_MESSAGE((int)*node->GetStackSize() == stacklimit, "Stack limit mismatch: " << ms << " (" << *node->GetStackSize() << " vs " << stacklimit << ")"); if (max_wit_size) BOOST_CHECK_MESSAGE(*node->GetWitnessSize() == *max_wit_size, "Witness size limit mismatch: " << ms << " (" << *node->GetWitnessSize() << " vs " << *max_wit_size << ")"); - TestSatisfy(ms, node); + if (stack_exec) BOOST_CHECK_MESSAGE(*node->GetExecStackSize() == *stack_exec, "Stack execution limit mismatch: " << ms << " (" << *node->GetExecStackSize() << " vs " << *stack_exec << ")"); + TestSatisfy(converter, ms, node); } } + +void Test(const std::string& ms, const std::string& hexscript, const std::string& hextapscript, int mode, + int opslimit, int stacklimit, std::optional<uint32_t> max_wit_size, + std::optional<uint32_t> max_tap_wit_size, + std::optional<uint32_t> stack_exec) +{ + KeyConverter wsh_converter(miniscript::MiniscriptContext::P2WSH); + Test(ms, hexscript, mode, wsh_converter, opslimit, stacklimit, max_wit_size, stack_exec); + KeyConverter tap_converter(miniscript::MiniscriptContext::TAPSCRIPT); + Test(ms, hextapscript == "=" ? hexscript : hextapscript, mode, tap_converter, opslimit, stacklimit, max_tap_wit_size, stack_exec); +} + +void Test(const std::string& ms, const std::string& hexscript, const std::string& hextapscript, int mode) +{ + Test(ms, hexscript, hextapscript, mode, + /*opslimit=*/-1, /*stacklimit=*/-1, + /*max_wit_size=*/std::nullopt, /*max_tap_wit_size=*/std::nullopt, /*stack_exec=*/std::nullopt); +} + } // namespace BOOST_FIXTURE_TEST_SUITE(miniscript_tests, BasicTestingSetup) @@ -391,114 +500,169 @@ BOOST_AUTO_TEST_CASE(fixed_tests) g_testdata.reset(new TestData()); // Validity rules - Test("l:older(1)", "?", TESTMODE_VALID | TESTMODE_NONMAL); // older(1): valid - Test("l:older(0)", "?", TESTMODE_INVALID); // older(0): k must be at least 1 - Test("l:older(2147483647)", "?", TESTMODE_VALID | TESTMODE_NONMAL); // older(2147483647): valid - Test("l:older(2147483648)", "?", TESTMODE_INVALID); // older(2147483648): k must be below 2^31 - Test("u:after(1)", "?", TESTMODE_VALID | TESTMODE_NONMAL); // after(1): valid - Test("u:after(0)", "?", TESTMODE_INVALID); // after(0): k must be at least 1 - Test("u:after(2147483647)", "?", TESTMODE_VALID | TESTMODE_NONMAL); // after(2147483647): valid - Test("u:after(2147483648)", "?", TESTMODE_INVALID); // after(2147483648): k must be below 2^31 - Test("andor(0,1,1)", "?", TESTMODE_VALID | TESTMODE_NONMAL); // andor(Bdu,B,B): valid - Test("andor(a:0,1,1)", "?", TESTMODE_INVALID); // andor(Wdu,B,B): X must be B - Test("andor(0,a:1,a:1)", "?", TESTMODE_INVALID); // andor(Bdu,W,W): Y and Z must be B/V/K - Test("andor(1,1,1)", "?", TESTMODE_INVALID); // andor(Bu,B,B): X must be d - Test("andor(n:or_i(0,after(1)),1,1)", "?", TESTMODE_VALID); // andor(Bdu,B,B): valid - Test("andor(or_i(0,after(1)),1,1)", "?", TESTMODE_INVALID); // andor(Bd,B,B): X must be u - Test("c:andor(0,pk_k(03a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c7),pk_k(036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a00))", "?", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG); // andor(Bdu,K,K): valid - Test("t:andor(0,v:1,v:1)", "?", TESTMODE_VALID | TESTMODE_NONMAL); // andor(Bdu,V,V): valid - Test("and_v(v:1,1)", "?", TESTMODE_VALID | TESTMODE_NONMAL); // and_v(V,B): valid - Test("t:and_v(v:1,v:1)", "?", TESTMODE_VALID | TESTMODE_NONMAL); // and_v(V,V): valid - Test("c:and_v(v:1,pk_k(036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a00))", "?", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG); // and_v(V,K): valid - Test("and_v(1,1)", "?", TESTMODE_INVALID); // and_v(B,B): X must be V - Test("and_v(pk_k(02352bbf4a4cdd12564f93fa332ce333301d9ad40271f8107181340aef25be59d5),1)", "?", TESTMODE_INVALID); // and_v(K,B): X must be V - Test("and_v(v:1,a:1)", "?", TESTMODE_INVALID); // and_v(K,W): Y must be B/V/K - Test("and_b(1,a:1)", "?", TESTMODE_VALID | TESTMODE_NONMAL); // and_b(B,W): valid - Test("and_b(1,1)", "?", TESTMODE_INVALID); // and_b(B,B): Y must W - Test("and_b(v:1,a:1)", "?", TESTMODE_INVALID); // and_b(V,W): X must be B - Test("and_b(a:1,a:1)", "?", TESTMODE_INVALID); // and_b(W,W): X must be B - Test("and_b(pk_k(025601570cb47f238d2b0286db4a990fa0f3ba28d1a319f5e7cf55c2a2444da7cc),a:1)", "?", TESTMODE_INVALID); // and_b(K,W): X must be B - Test("or_b(0,a:0)", "?", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG); // or_b(Bd,Wd): valid - Test("or_b(1,a:0)", "?", TESTMODE_INVALID); // or_b(B,Wd): X must be d - Test("or_b(0,a:1)", "?", TESTMODE_INVALID); // or_b(Bd,W): Y must be d - Test("or_b(0,0)", "?", TESTMODE_INVALID); // or_b(Bd,Bd): Y must W - Test("or_b(v:0,a:0)", "?", TESTMODE_INVALID); // or_b(V,Wd): X must be B - Test("or_b(a:0,a:0)", "?", TESTMODE_INVALID); // or_b(Wd,Wd): X must be B - Test("or_b(pk_k(025601570cb47f238d2b0286db4a990fa0f3ba28d1a319f5e7cf55c2a2444da7cc),a:0)", "?", TESTMODE_INVALID); // or_b(Kd,Wd): X must be B - Test("t:or_c(0,v:1)", "?", TESTMODE_VALID | TESTMODE_NONMAL); // or_c(Bdu,V): valid - Test("t:or_c(a:0,v:1)", "?", TESTMODE_INVALID); // or_c(Wdu,V): X must be B - Test("t:or_c(1,v:1)", "?", TESTMODE_INVALID); // or_c(Bu,V): X must be d - Test("t:or_c(n:or_i(0,after(1)),v:1)", "?", TESTMODE_VALID); // or_c(Bdu,V): valid - Test("t:or_c(or_i(0,after(1)),v:1)", "?", TESTMODE_INVALID); // or_c(Bd,V): X must be u - Test("t:or_c(0,1)", "?", TESTMODE_INVALID); // or_c(Bdu,B): Y must be V - Test("or_d(0,1)", "?", TESTMODE_VALID | TESTMODE_NONMAL); // or_d(Bdu,B): valid - Test("or_d(a:0,1)", "?", TESTMODE_INVALID); // or_d(Wdu,B): X must be B - Test("or_d(1,1)", "?", TESTMODE_INVALID); // or_d(Bu,B): X must be d - Test("or_d(n:or_i(0,after(1)),1)", "?", TESTMODE_VALID); // or_d(Bdu,B): valid - Test("or_d(or_i(0,after(1)),1)", "?", TESTMODE_INVALID); // or_d(Bd,B): X must be u - Test("or_d(0,v:1)", "?", TESTMODE_INVALID); // or_d(Bdu,V): Y must be B - Test("or_i(1,1)", "?", TESTMODE_VALID); // or_i(B,B): valid - Test("t:or_i(v:1,v:1)", "?", TESTMODE_VALID); // or_i(V,V): valid - Test("c:or_i(pk_k(03a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c7),pk_k(036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a00))", "?", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG); // or_i(K,K): valid - Test("or_i(a:1,a:1)", "?", TESTMODE_INVALID); // or_i(W,W): X and Y must be B/V/K - Test("or_b(l:after(100),al:after(1000000000))", "?", TESTMODE_VALID); // or_b(timelock, heighlock) valid - Test("and_b(after(100),a:after(1000000000))", "?", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_TIMELOCKMIX); // and_b(timelock, heighlock) invalid - Test("pk(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65)", "2103d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65ac", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG); // alias to c:pk_k - Test("pkh(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65)", "76a914fcd35ddacad9f2d5be5e464639441c6065e6955d88ac", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG); // alias to c:pk_h - + Test("l:older(1)", "?", "?", TESTMODE_VALID | TESTMODE_NONMAL); // older(1): valid + Test("l:older(0)", "?", "?", TESTMODE_INVALID); // older(0): k must be at least 1 + Test("l:older(2147483647)", "?", "?", TESTMODE_VALID | TESTMODE_NONMAL); // older(2147483647): valid + Test("l:older(2147483648)", "?", "?", TESTMODE_INVALID); // older(2147483648): k must be below 2^31 + Test("u:after(1)", "?", "?", TESTMODE_VALID | TESTMODE_NONMAL); // after(1): valid + Test("u:after(0)", "?", "?", TESTMODE_INVALID); // after(0): k must be at least 1 + Test("u:after(2147483647)", "?", "?", TESTMODE_VALID | TESTMODE_NONMAL); // after(2147483647): valid + Test("u:after(2147483648)", "?", "?", TESTMODE_INVALID); // after(2147483648): k must be below 2^31 + Test("andor(0,1,1)", "?", "?", TESTMODE_VALID | TESTMODE_NONMAL); // andor(Bdu,B,B): valid + Test("andor(a:0,1,1)", "?", "?", TESTMODE_INVALID); // andor(Wdu,B,B): X must be B + Test("andor(0,a:1,a:1)", "?", "?", TESTMODE_INVALID); // andor(Bdu,W,W): Y and Z must be B/V/K + Test("andor(1,1,1)", "?", "?", TESTMODE_INVALID); // andor(Bu,B,B): X must be d + Test("andor(n:or_i(0,after(1)),1,1)", "?", "?", TESTMODE_VALID); // andor(Bdu,B,B): valid + Test("andor(or_i(0,after(1)),1,1)", "?", "?", TESTMODE_INVALID); // andor(Bd,B,B): X must be u + Test("c:andor(0,pk_k(03a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c7),pk_k(036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a00))", "?", "?", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG); // andor(Bdu,K,K): valid + Test("t:andor(0,v:1,v:1)", "?", "?", TESTMODE_VALID | TESTMODE_NONMAL); // andor(Bdu,V,V): valid + Test("and_v(v:1,1)", "?", "?", TESTMODE_VALID | TESTMODE_NONMAL); // and_v(V,B): valid + Test("t:and_v(v:1,v:1)", "?", "?", TESTMODE_VALID | TESTMODE_NONMAL); // and_v(V,V): valid + Test("c:and_v(v:1,pk_k(036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a00))", "?", "?", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG); // and_v(V,K): valid + Test("and_v(1,1)", "?", "?", TESTMODE_INVALID); // and_v(B,B): X must be V + Test("and_v(pk_k(02352bbf4a4cdd12564f93fa332ce333301d9ad40271f8107181340aef25be59d5),1)", "?", "?", TESTMODE_INVALID); // and_v(K,B): X must be V + Test("and_v(v:1,a:1)", "?", "?", TESTMODE_INVALID); // and_v(K,W): Y must be B/V/K + Test("and_b(1,a:1)", "?", "?", TESTMODE_VALID | TESTMODE_NONMAL); // and_b(B,W): valid + Test("and_b(1,1)", "?", "?", TESTMODE_INVALID); // and_b(B,B): Y must W + Test("and_b(v:1,a:1)", "?", "?", TESTMODE_INVALID); // and_b(V,W): X must be B + Test("and_b(a:1,a:1)", "?", "?", TESTMODE_INVALID); // and_b(W,W): X must be B + Test("and_b(pk_k(025601570cb47f238d2b0286db4a990fa0f3ba28d1a319f5e7cf55c2a2444da7cc),a:1)", "?", "?", TESTMODE_INVALID); // and_b(K,W): X must be B + Test("or_b(0,a:0)", "?", "?", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG); // or_b(Bd,Wd): valid + Test("or_b(1,a:0)", "?", "?", TESTMODE_INVALID); // or_b(B,Wd): X must be d + Test("or_b(0,a:1)", "?", "?", TESTMODE_INVALID); // or_b(Bd,W): Y must be d + Test("or_b(0,0)", "?", "?", TESTMODE_INVALID); // or_b(Bd,Bd): Y must W + Test("or_b(v:0,a:0)", "?", "?", TESTMODE_INVALID); // or_b(V,Wd): X must be B + Test("or_b(a:0,a:0)", "?", "?", TESTMODE_INVALID); // or_b(Wd,Wd): X must be B + Test("or_b(pk_k(025601570cb47f238d2b0286db4a990fa0f3ba28d1a319f5e7cf55c2a2444da7cc),a:0)", "?", "?", TESTMODE_INVALID); // or_b(Kd,Wd): X must be B + Test("t:or_c(0,v:1)", "?", "?", TESTMODE_VALID | TESTMODE_NONMAL); // or_c(Bdu,V): valid + Test("t:or_c(a:0,v:1)", "?", "?", TESTMODE_INVALID); // or_c(Wdu,V): X must be B + Test("t:or_c(1,v:1)", "?", "?", TESTMODE_INVALID); // or_c(Bu,V): X must be d + Test("t:or_c(n:or_i(0,after(1)),v:1)", "?", "?", TESTMODE_VALID); // or_c(Bdu,V): valid + Test("t:or_c(or_i(0,after(1)),v:1)", "?", "?", TESTMODE_INVALID); // or_c(Bd,V): X must be u + Test("t:or_c(0,1)", "?", "?", TESTMODE_INVALID); // or_c(Bdu,B): Y must be V + Test("or_d(0,1)", "?", "?", TESTMODE_VALID | TESTMODE_NONMAL); // or_d(Bdu,B): valid + Test("or_d(a:0,1)", "?", "?", TESTMODE_INVALID); // or_d(Wdu,B): X must be B + Test("or_d(1,1)", "?", "?", TESTMODE_INVALID); // or_d(Bu,B): X must be d + Test("or_d(n:or_i(0,after(1)),1)", "?", "?", TESTMODE_VALID); // or_d(Bdu,B): valid + Test("or_d(or_i(0,after(1)),1)", "?", "?", TESTMODE_INVALID); // or_d(Bd,B): X must be u + Test("or_d(0,v:1)", "?", "?", TESTMODE_INVALID); // or_d(Bdu,V): Y must be B + Test("or_i(1,1)", "?", "?", TESTMODE_VALID); // or_i(B,B): valid + Test("t:or_i(v:1,v:1)", "?", "?", TESTMODE_VALID); // or_i(V,V): valid + Test("c:or_i(pk_k(03a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c7),pk_k(036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a00))", "?", "?", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG); // or_i(K,K): valid + Test("or_i(a:1,a:1)", "?", "?", TESTMODE_INVALID); // or_i(W,W): X and Y must be B/V/K + Test("or_b(l:after(100),al:after(1000000000))", "?", "?", TESTMODE_VALID); // or_b(timelock, heighlock) valid + Test("and_b(after(100),a:after(1000000000))", "?", "?", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_TIMELOCKMIX); // and_b(timelock, heighlock) invalid + Test("pk(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65)", "2103d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65ac", "20d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65ac", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG); // alias to c:pk_k + Test("pkh(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65)", "76a914fcd35ddacad9f2d5be5e464639441c6065e6955d88ac", "76a914fd1690c37fa3b0f04395ddc9415b220ab1ccc59588ac", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG); // alias to c:pk_h // Randomly generated test set that covers the majority of type and node type combinations - Test("lltvln:after(1231488000)", "6300676300676300670400046749b1926869516868", TESTMODE_VALID | TESTMODE_NONMAL, 12, 3, 3); - Test("uuj:and_v(v:multi(2,03d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a,025601570cb47f238d2b0286db4a990fa0f3ba28d1a319f5e7cf55c2a2444da7cc),after(1231488000))", "6363829263522103d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a21025601570cb47f238d2b0286db4a990fa0f3ba28d1a319f5e7cf55c2a2444da7cc52af0400046749b168670068670068", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG, 14, 5, 2 + 2 + 1 + 2 * 73); - Test("or_b(un:multi(2,03daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729,024ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c97),al:older(16))", "63522103daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee872921024ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c9752ae926700686b63006760b2686c9b", TESTMODE_VALID, 14, 5, 2 + 1 + 2 * 73 + 2); - Test("j:and_v(vdv:after(1567547623),older(2016))", "829263766304e7e06e5db169686902e007b268", TESTMODE_VALID | TESTMODE_NONMAL, 11, 1, 2); - Test("t:and_v(vu:hash256(131772552c01444cd81360818376a040b7c3b2b7b0a53550ee3edde216cec61b),v:sha256(ec4916dd28fc4c10d78e287ca5d9cc51ee1ae73cbfde08c6b37324cbfaac8bc5))", "6382012088aa20131772552c01444cd81360818376a040b7c3b2b7b0a53550ee3edde216cec61b876700686982012088a820ec4916dd28fc4c10d78e287ca5d9cc51ee1ae73cbfde08c6b37324cbfaac8bc58851", TESTMODE_VALID | TESTMODE_NONMAL, 12, 3, 2 + 33 + 33); - Test("t:andor(multi(3,02d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e,03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556,02e493dbf1c10d80f3581e4904930b1404cc6c13900ee0758474fa94abe8c4cd13),v:older(4194305),v:sha256(9267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed2))", "532102d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e2103fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a14602975562102e493dbf1c10d80f3581e4904930b1404cc6c13900ee0758474fa94abe8c4cd1353ae6482012088a8209267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed2886703010040b2696851", TESTMODE_VALID | TESTMODE_NONMAL, 13, 5, 1 + 3 * 73); - Test("or_d(multi(1,02f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9),or_b(multi(3,022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01,032fa2104d6b38d11b0230010559879124e42ab8dfeff5ff29dc9cdadd4ecacc3f,03d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a),su:after(500000)))", "512102f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f951ae73645321022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a0121032fa2104d6b38d11b0230010559879124e42ab8dfeff5ff29dc9cdadd4ecacc3f2103d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a53ae7c630320a107b16700689b68", TESTMODE_VALID | TESTMODE_NONMAL, 15, 7, 2 + 1 + 3 * 73 + 1); - Test("or_d(sha256(38df1c1f64a24a77b23393bca50dff872e31edc4f3b5aa3b90ad0b82f4f089b6),and_n(un:after(499999999),older(4194305)))", "82012088a82038df1c1f64a24a77b23393bca50dff872e31edc4f3b5aa3b90ad0b82f4f089b68773646304ff64cd1db19267006864006703010040b26868", TESTMODE_VALID, 16, 1, 33); - Test("and_v(or_i(v:multi(2,02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5,03774ae7f858a9411e5ef4246b70c65aac5649980be5c17891bbec17895da008cb),v:multi(2,03e60fce93b59e9ec53011aabc21c23e97b2a31369b87a5ae9c44ee89e2a6dec0a,025cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bc)),sha256(d1ec675902ef1633427ca360b290b0b3045a0d9058ddb5e648b4c3c3224c5c68))", "63522102c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee52103774ae7f858a9411e5ef4246b70c65aac5649980be5c17891bbec17895da008cb52af67522103e60fce93b59e9ec53011aabc21c23e97b2a31369b87a5ae9c44ee89e2a6dec0a21025cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bc52af6882012088a820d1ec675902ef1633427ca360b290b0b3045a0d9058ddb5e648b4c3c3224c5c6887", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG, 11, 5, 2 + 1 + 2 * 73 + 33); - Test("j:and_b(multi(2,0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798,024ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c97),s:or_i(older(1),older(4252898)))", "82926352210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179821024ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c9752ae7c6351b26703e2e440b2689a68", TESTMODE_VALID | TESTMODE_NEEDSIG, 14, 4, 1 + 2 * 73 + 2); - Test("and_b(older(16),s:or_d(sha256(e38990d0c7fc009880a9c07c23842e886c6bbdc964ce6bdd5817ad357335ee6f),n:after(1567547623)))", "60b27c82012088a820e38990d0c7fc009880a9c07c23842e886c6bbdc964ce6bdd5817ad357335ee6f87736404e7e06e5db192689a", TESTMODE_VALID, 12, 1, 33); - Test("j:and_v(v:hash160(20195b5a3d650c17f0f29f91c33f8f6335193d07),or_d(sha256(96de8fc8c256fa1e1556d41af431cace7dca68707c78dd88c3acab8b17164c47),older(16)))", "82926382012088a91420195b5a3d650c17f0f29f91c33f8f6335193d078882012088a82096de8fc8c256fa1e1556d41af431cace7dca68707c78dd88c3acab8b17164c4787736460b26868", TESTMODE_VALID, 16, 2, 33 + 33); - Test("and_b(hash256(32ba476771d01e37807990ead8719f08af494723de1d228f2c2c07cc0aa40bac),a:and_b(hash256(131772552c01444cd81360818376a040b7c3b2b7b0a53550ee3edde216cec61b),a:older(1)))", "82012088aa2032ba476771d01e37807990ead8719f08af494723de1d228f2c2c07cc0aa40bac876b82012088aa20131772552c01444cd81360818376a040b7c3b2b7b0a53550ee3edde216cec61b876b51b26c9a6c9a", TESTMODE_VALID | TESTMODE_NONMAL, 15, 2, 33 + 33); - Test("thresh(2,multi(2,03a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c7,036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a00),a:multi(1,036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a00),ac:pk_k(022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01))", "522103a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c721036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a0052ae6b5121036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a0051ae6c936b21022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01ac6c935287", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG, 13, 6, 1 + 2 * 73 + 1 + 73 + 1); - Test("and_n(sha256(d1ec675902ef1633427ca360b290b0b3045a0d9058ddb5e648b4c3c3224c5c68),t:or_i(v:older(4252898),v:older(144)))", "82012088a820d1ec675902ef1633427ca360b290b0b3045a0d9058ddb5e648b4c3c3224c5c68876400676303e2e440b26967029000b269685168", TESTMODE_VALID, 14, 2, 33 + 2); - Test("or_d(nd:and_v(v:older(4252898),v:older(4252898)),sha256(38df1c1f64a24a77b23393bca50dff872e31edc4f3b5aa3b90ad0b82f4f089b6))", "766303e2e440b26903e2e440b2696892736482012088a82038df1c1f64a24a77b23393bca50dff872e31edc4f3b5aa3b90ad0b82f4f089b68768", TESTMODE_VALID, 15, 2, 1 + 33); - Test("c:and_v(or_c(sha256(9267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed2),v:multi(1,02c44d12c7065d812e8acf28d7cbb19f9011ecd9e9fdf281b0e6a3b5e87d22e7db)),pk_k(03acd484e2f0c7f65309ad178a9f559abde09796974c57e714c35f110dfc27ccbe))", "82012088a8209267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed28764512102c44d12c7065d812e8acf28d7cbb19f9011ecd9e9fdf281b0e6a3b5e87d22e7db51af682103acd484e2f0c7f65309ad178a9f559abde09796974c57e714c35f110dfc27ccbeac", TESTMODE_VALID | TESTMODE_NEEDSIG, 8, 2, 33 + 73); - Test("c:and_v(or_c(multi(2,036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a00,02352bbf4a4cdd12564f93fa332ce333301d9ad40271f8107181340aef25be59d5),v:ripemd160(1b0f3c404d12075c68c938f9f60ebea4f74941a0)),pk_k(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556))", "5221036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a002102352bbf4a4cdd12564f93fa332ce333301d9ad40271f8107181340aef25be59d552ae6482012088a6141b0f3c404d12075c68c938f9f60ebea4f74941a088682103fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556ac", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG, 10, 5, 1 + 2 * 73 + 73); - Test("and_v(andor(hash256(8a35d9ca92a48eaade6f53a64985e9e2afeb74dcf8acb4c3721e0dc7e4294b25),v:hash256(939894f70e6c3a25da75da0cc2071b4076d9b006563cf635986ada2e93c0d735),v:older(50000)),after(499999999))", "82012088aa208a35d9ca92a48eaade6f53a64985e9e2afeb74dcf8acb4c3721e0dc7e4294b2587640350c300b2696782012088aa20939894f70e6c3a25da75da0cc2071b4076d9b006563cf635986ada2e93c0d735886804ff64cd1db1", TESTMODE_VALID, 14, 2, 33 + 33); - Test("andor(hash256(5f8d30e655a7ba0d7596bb3ddfb1d2d20390d23b1845000e1e118b3be1b3f040),j:and_v(v:hash160(3a2bff0da9d96868e66abc4427bea4691cf61ccd),older(4194305)),ripemd160(44d90e2d3714c8663b632fcf0f9d5f22192cc4c8))", "82012088aa205f8d30e655a7ba0d7596bb3ddfb1d2d20390d23b1845000e1e118b3be1b3f040876482012088a61444d90e2d3714c8663b632fcf0f9d5f22192cc4c8876782926382012088a9143a2bff0da9d96868e66abc4427bea4691cf61ccd8803010040b26868", TESTMODE_VALID, 20, 2, 33 + 33); - Test("or_i(c:and_v(v:after(500000),pk_k(02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5)),sha256(d9147961436944f43cd99d28b2bbddbf452ef872b30c8279e255e7daafc7f946))", "630320a107b1692102c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5ac6782012088a820d9147961436944f43cd99d28b2bbddbf452ef872b30c8279e255e7daafc7f9468768", TESTMODE_VALID | TESTMODE_NONMAL, 10, 2, 2 + 73); - Test("thresh(2,c:pk_h(025cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bc),s:sha256(e38990d0c7fc009880a9c07c23842e886c6bbdc964ce6bdd5817ad357335ee6f),a:hash160(dd69735817e0e3f6f826a9238dc2e291184f0131))", "76a9145dedfbf9ea599dd4e3ca6a80b333c472fd0b3f6988ac7c82012088a820e38990d0c7fc009880a9c07c23842e886c6bbdc964ce6bdd5817ad357335ee6f87936b82012088a914dd69735817e0e3f6f826a9238dc2e291184f0131876c935287", TESTMODE_VALID, 18, 4, 1 + 34 + 33 + 33); - Test("and_n(sha256(9267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed2),uc:and_v(v:older(144),pk_k(03fe72c435413d33d48ac09c9161ba8b09683215439d62b7940502bda8b202e6ce)))", "82012088a8209267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed28764006763029000b2692103fe72c435413d33d48ac09c9161ba8b09683215439d62b7940502bda8b202e6ceac67006868", TESTMODE_VALID | TESTMODE_NEEDSIG, 13, 3, 33 + 2 + 73); - Test("and_n(c:pk_k(03daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729),and_b(l:older(4252898),a:older(16)))", "2103daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729ac64006763006703e2e440b2686b60b26c9a68", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG | TESTMODE_TIMELOCKMIX, 12, 2, 73 + 1); - Test("c:or_i(and_v(v:older(16),pk_h(02d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e)),pk_h(026a245bf6dc698504c89a20cfded60853152b695336c28063b61c65cbd269e6b4))", "6360b26976a9149fc5dbe5efdce10374a4dd4053c93af540211718886776a9142fbd32c8dd59ee7c17e66cb6ebea7e9846c3040f8868ac", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG, 12, 3, 2 + 34 + 73); - Test("or_d(c:pk_h(02e493dbf1c10d80f3581e4904930b1404cc6c13900ee0758474fa94abe8c4cd13),andor(c:pk_k(024ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c97),older(2016),after(1567547623)))", "76a914c42e7ef92fdb603af844d064faad95db9bcdfd3d88ac736421024ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c97ac6404e7e06e5db16702e007b26868", TESTMODE_VALID | TESTMODE_NONMAL, 13, 3, 1 + 34 + 73); - Test("c:andor(ripemd160(6ad07d21fd5dfc646f0b30577045ce201616b9ba),pk_h(02d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e),and_v(v:hash256(8a35d9ca92a48eaade6f53a64985e9e2afeb74dcf8acb4c3721e0dc7e4294b25),pk_h(03d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a)))", "82012088a6146ad07d21fd5dfc646f0b30577045ce201616b9ba876482012088aa208a35d9ca92a48eaade6f53a64985e9e2afeb74dcf8acb4c3721e0dc7e4294b258876a914dd100be7d9aea5721158ebde6d6a1fd8fff93bb1886776a9149fc5dbe5efdce10374a4dd4053c93af5402117188868ac", TESTMODE_VALID | TESTMODE_NEEDSIG, 18, 3, 33 + 34 + 73); - Test("c:andor(u:ripemd160(6ad07d21fd5dfc646f0b30577045ce201616b9ba),pk_h(03daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729),or_i(pk_h(022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01),pk_h(0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798)))", "6382012088a6146ad07d21fd5dfc646f0b30577045ce201616b9ba87670068646376a9149652d86bedf43ad264362e6e6eba6eb764508127886776a914751e76e8199196d454941c45d1b3a323f1433bd688686776a91420d637c1a6404d2227f3561fdbaff5a680dba6488868ac", TESTMODE_VALID | TESTMODE_NEEDSIG, 23, 4, 2 + 33 + 34 + 73); - Test("c:or_i(andor(c:pk_h(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),pk_h(022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01),pk_h(02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5)),pk_k(02d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e))", "6376a914fcd35ddacad9f2d5be5e464639441c6065e6955d88ac6476a91406afd46bcdfd22ef94ac122aa11f241244a37ecc886776a9149652d86bedf43ad264362e6e6eba6eb7645081278868672102d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e68ac", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG, 17, 5, 2 + 34 + 73 + 34 + 73); - Test("thresh(1,c:pk_k(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),altv:after(1000000000),altv:after(100))", "2103d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65ac6b6300670400ca9a3bb16951686c936b6300670164b16951686c935187", TESTMODE_VALID, 18, 3, 73 + 2 + 2); - Test("thresh(2,c:pk_k(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),ac:pk_k(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556),altv:after(1000000000),altv:after(100))", "2103d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65ac6b2103fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556ac6c936b6300670400ca9a3bb16951686c936b6300670164b16951686c935287", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_TIMELOCKMIX, 22, 4, 73 + 73 + 2 + 2); + Test("lltvln:after(1231488000)", "6300676300676300670400046749b1926869516868", "=", TESTMODE_VALID | TESTMODE_NONMAL, 12, 3, 3, 3, 3); + Test("uuj:and_v(v:multi(2,03d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a,025601570cb47f238d2b0286db4a990fa0f3ba28d1a319f5e7cf55c2a2444da7cc),after(1231488000))", "6363829263522103d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a21025601570cb47f238d2b0286db4a990fa0f3ba28d1a319f5e7cf55c2a2444da7cc52af0400046749b168670068670068", "?", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG | TESTMODE_TAPSCRIPT_INVALID, 14, 5, 2 + 2 + 1 + 2 * 73, 0, 7); + Test("or_b(un:multi(2,03daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729,024ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c97),al:older(16))", "63522103daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee872921024ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c9752ae926700686b63006760b2686c9b", "?", TESTMODE_VALID | TESTMODE_TAPSCRIPT_INVALID, 14, 5, 2 + 1 + 2 * 73 + 2, 0, 8); + Test("j:and_v(vdv:after(1567547623),older(2016))", "829263766304e7e06e5db169686902e007b268", "=", TESTMODE_VALID | TESTMODE_NONMAL, 11, 1, 2, 2, 2); + Test("t:and_v(vu:hash256(131772552c01444cd81360818376a040b7c3b2b7b0a53550ee3edde216cec61b),v:sha256(ec4916dd28fc4c10d78e287ca5d9cc51ee1ae73cbfde08c6b37324cbfaac8bc5))", "6382012088aa20131772552c01444cd81360818376a040b7c3b2b7b0a53550ee3edde216cec61b876700686982012088a820ec4916dd28fc4c10d78e287ca5d9cc51ee1ae73cbfde08c6b37324cbfaac8bc58851", "6382012088aa20131772552c01444cd81360818376a040b7c3b2b7b0a53550ee3edde216cec61b876700686982012088a820ec4916dd28fc4c10d78e287ca5d9cc51ee1ae73cbfde08c6b37324cbfaac8bc58851", TESTMODE_VALID | TESTMODE_NONMAL, 12, 3, 2 + 33 + 33, 2 + 33 + 33, 4); + Test("t:andor(multi(3,02d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e,03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556,02e493dbf1c10d80f3581e4904930b1404cc6c13900ee0758474fa94abe8c4cd13),v:older(4194305),v:sha256(9267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed2))", "532102d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e2103fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a14602975562102e493dbf1c10d80f3581e4904930b1404cc6c13900ee0758474fa94abe8c4cd1353ae6482012088a8209267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed2886703010040b2696851", "?", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_TAPSCRIPT_INVALID, 13, 5, 1 + 3 * 73, 0, 10); + Test("or_d(multi(1,02f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9),or_b(multi(3,022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01,032fa2104d6b38d11b0230010559879124e42ab8dfeff5ff29dc9cdadd4ecacc3f,03d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a),su:after(500000)))", "512102f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f951ae73645321022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a0121032fa2104d6b38d11b0230010559879124e42ab8dfeff5ff29dc9cdadd4ecacc3f2103d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a53ae7c630320a107b16700689b68", "?", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_TAPSCRIPT_INVALID, 15, 7, 2 + 1 + 3 * 73 + 1, 0, 10); + Test("or_d(sha256(38df1c1f64a24a77b23393bca50dff872e31edc4f3b5aa3b90ad0b82f4f089b6),and_n(un:after(499999999),older(4194305)))", "82012088a82038df1c1f64a24a77b23393bca50dff872e31edc4f3b5aa3b90ad0b82f4f089b68773646304ff64cd1db19267006864006703010040b26868", "82012088a82038df1c1f64a24a77b23393bca50dff872e31edc4f3b5aa3b90ad0b82f4f089b68773646304ff64cd1db19267006864006703010040b26868", TESTMODE_VALID, 16, 1, 33, 33, 3); + Test("and_v(or_i(v:multi(2,02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5,03774ae7f858a9411e5ef4246b70c65aac5649980be5c17891bbec17895da008cb),v:multi(2,03e60fce93b59e9ec53011aabc21c23e97b2a31369b87a5ae9c44ee89e2a6dec0a,025cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bc)),sha256(d1ec675902ef1633427ca360b290b0b3045a0d9058ddb5e648b4c3c3224c5c68))", "63522102c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee52103774ae7f858a9411e5ef4246b70c65aac5649980be5c17891bbec17895da008cb52af67522103e60fce93b59e9ec53011aabc21c23e97b2a31369b87a5ae9c44ee89e2a6dec0a21025cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bc52af6882012088a820d1ec675902ef1633427ca360b290b0b3045a0d9058ddb5e648b4c3c3224c5c6887", "?", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG | TESTMODE_TAPSCRIPT_INVALID, 11, 5, 2 + 1 + 2 * 73 + 33, 0, 8); + Test("j:and_b(multi(2,0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798,024ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c97),s:or_i(older(1),older(4252898)))", "82926352210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179821024ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c9752ae7c6351b26703e2e440b2689a68", "?", TESTMODE_VALID | TESTMODE_NEEDSIG | TESTMODE_TAPSCRIPT_INVALID, 14, 4, 1 + 2 * 73 + 2, 0, 8); + Test("and_b(older(16),s:or_d(sha256(e38990d0c7fc009880a9c07c23842e886c6bbdc964ce6bdd5817ad357335ee6f),n:after(1567547623)))", "60b27c82012088a820e38990d0c7fc009880a9c07c23842e886c6bbdc964ce6bdd5817ad357335ee6f87736404e7e06e5db192689a", "=", TESTMODE_VALID, 12, 1, 33, 33, 4); + Test("j:and_v(v:hash160(20195b5a3d650c17f0f29f91c33f8f6335193d07),or_d(sha256(96de8fc8c256fa1e1556d41af431cace7dca68707c78dd88c3acab8b17164c47),older(16)))", "82926382012088a91420195b5a3d650c17f0f29f91c33f8f6335193d078882012088a82096de8fc8c256fa1e1556d41af431cace7dca68707c78dd88c3acab8b17164c4787736460b26868", "=", TESTMODE_VALID, 16, 2, 33 + 33, 33 + 33, 4); + Test("and_b(hash256(32ba476771d01e37807990ead8719f08af494723de1d228f2c2c07cc0aa40bac),a:and_b(hash256(131772552c01444cd81360818376a040b7c3b2b7b0a53550ee3edde216cec61b),a:older(1)))", "82012088aa2032ba476771d01e37807990ead8719f08af494723de1d228f2c2c07cc0aa40bac876b82012088aa20131772552c01444cd81360818376a040b7c3b2b7b0a53550ee3edde216cec61b876b51b26c9a6c9a", "=", TESTMODE_VALID | TESTMODE_NONMAL, 15, 2, 33 + 33, 33 + 33, 4); + Test("thresh(2,multi(2,03a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c7,036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a00),a:multi(1,036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a00),ac:pk_k(022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01))", "522103a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c721036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a0052ae6b5121036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a0051ae6c936b21022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01ac6c935287", "?", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG | TESTMODE_TAPSCRIPT_INVALID, 13, 6, 1 + 2 * 73 + 1 + 73 + 1, 0, 10); + Test("and_n(sha256(d1ec675902ef1633427ca360b290b0b3045a0d9058ddb5e648b4c3c3224c5c68),t:or_i(v:older(4252898),v:older(144)))", "82012088a820d1ec675902ef1633427ca360b290b0b3045a0d9058ddb5e648b4c3c3224c5c68876400676303e2e440b26967029000b269685168", "=", TESTMODE_VALID, 14, 2, 33 + 2, 33 + 2, 4); + Test("or_d(nd:and_v(v:older(4252898),v:older(4252898)),sha256(38df1c1f64a24a77b23393bca50dff872e31edc4f3b5aa3b90ad0b82f4f089b6))", "766303e2e440b26903e2e440b2696892736482012088a82038df1c1f64a24a77b23393bca50dff872e31edc4f3b5aa3b90ad0b82f4f089b68768", "=", TESTMODE_VALID, 15, 2, 1 + 33, 1 + 33, 3); + Test("c:and_v(or_c(sha256(9267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed2),v:multi(1,02c44d12c7065d812e8acf28d7cbb19f9011ecd9e9fdf281b0e6a3b5e87d22e7db)),pk_k(03acd484e2f0c7f65309ad178a9f559abde09796974c57e714c35f110dfc27ccbe))", "82012088a8209267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed28764512102c44d12c7065d812e8acf28d7cbb19f9011ecd9e9fdf281b0e6a3b5e87d22e7db51af682103acd484e2f0c7f65309ad178a9f559abde09796974c57e714c35f110dfc27ccbeac", "?", TESTMODE_VALID | TESTMODE_NEEDSIG | TESTMODE_TAPSCRIPT_INVALID, 8, 2, 33 + 73, 0, 4); + Test("c:and_v(or_c(multi(2,036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a00,02352bbf4a4cdd12564f93fa332ce333301d9ad40271f8107181340aef25be59d5),v:ripemd160(1b0f3c404d12075c68c938f9f60ebea4f74941a0)),pk_k(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556))", "5221036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a002102352bbf4a4cdd12564f93fa332ce333301d9ad40271f8107181340aef25be59d552ae6482012088a6141b0f3c404d12075c68c938f9f60ebea4f74941a088682103fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556ac", "?", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG | TESTMODE_TAPSCRIPT_INVALID, 10, 5, 1 + 2 * 73 + 73, 0, 9); + Test("and_v(andor(hash256(8a35d9ca92a48eaade6f53a64985e9e2afeb74dcf8acb4c3721e0dc7e4294b25),v:hash256(939894f70e6c3a25da75da0cc2071b4076d9b006563cf635986ada2e93c0d735),v:older(50000)),after(499999999))", "82012088aa208a35d9ca92a48eaade6f53a64985e9e2afeb74dcf8acb4c3721e0dc7e4294b2587640350c300b2696782012088aa20939894f70e6c3a25da75da0cc2071b4076d9b006563cf635986ada2e93c0d735886804ff64cd1db1", "=", TESTMODE_VALID, 14, 2, 33 + 33, 33 + 33, 4); + Test("andor(hash256(5f8d30e655a7ba0d7596bb3ddfb1d2d20390d23b1845000e1e118b3be1b3f040),j:and_v(v:hash160(3a2bff0da9d96868e66abc4427bea4691cf61ccd),older(4194305)),ripemd160(44d90e2d3714c8663b632fcf0f9d5f22192cc4c8))", "82012088aa205f8d30e655a7ba0d7596bb3ddfb1d2d20390d23b1845000e1e118b3be1b3f040876482012088a61444d90e2d3714c8663b632fcf0f9d5f22192cc4c8876782926382012088a9143a2bff0da9d96868e66abc4427bea4691cf61ccd8803010040b26868", "=", TESTMODE_VALID, 20, 2, 33 + 33, 33 + 33, 4); + Test("or_i(c:and_v(v:after(500000),pk_k(02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5)),sha256(d9147961436944f43cd99d28b2bbddbf452ef872b30c8279e255e7daafc7f946))", "630320a107b1692102c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5ac6782012088a820d9147961436944f43cd99d28b2bbddbf452ef872b30c8279e255e7daafc7f9468768", "630320a107b16920c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5ac6782012088a820d9147961436944f43cd99d28b2bbddbf452ef872b30c8279e255e7daafc7f9468768", TESTMODE_VALID | TESTMODE_NONMAL, 10, 2, 2 + 73, 2 + 66, 3); + Test("thresh(2,c:pk_h(025cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bc),s:sha256(e38990d0c7fc009880a9c07c23842e886c6bbdc964ce6bdd5817ad357335ee6f),a:hash160(dd69735817e0e3f6f826a9238dc2e291184f0131))", "76a9145dedfbf9ea599dd4e3ca6a80b333c472fd0b3f6988ac7c82012088a820e38990d0c7fc009880a9c07c23842e886c6bbdc964ce6bdd5817ad357335ee6f87936b82012088a914dd69735817e0e3f6f826a9238dc2e291184f0131876c935287", "76a9141a7ac36cfa8431ab2395d701b0050045ae4a37d188ac7c82012088a820e38990d0c7fc009880a9c07c23842e886c6bbdc964ce6bdd5817ad357335ee6f87936b82012088a914dd69735817e0e3f6f826a9238dc2e291184f0131876c935287", TESTMODE_VALID, 18, 4, 1 + 34 + 33 + 33, 1 + 33 + 33 + 33, 6); + Test("and_n(sha256(9267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed2),uc:and_v(v:older(144),pk_k(03fe72c435413d33d48ac09c9161ba8b09683215439d62b7940502bda8b202e6ce)))", "82012088a8209267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed28764006763029000b2692103fe72c435413d33d48ac09c9161ba8b09683215439d62b7940502bda8b202e6ceac67006868", "82012088a8209267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed28764006763029000b26920fe72c435413d33d48ac09c9161ba8b09683215439d62b7940502bda8b202e6ceac67006868", TESTMODE_VALID | TESTMODE_NEEDSIG, 13, 3, 33 + 2 + 73, 33 + 2 + 66, 5); + Test("and_n(c:pk_k(03daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729),and_b(l:older(4252898),a:older(16)))", "2103daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729ac64006763006703e2e440b2686b60b26c9a68", "20daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729ac64006763006703e2e440b2686b60b26c9a68", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG | TESTMODE_TIMELOCKMIX, 12, 2, 73 + 1, 66 + 1, 3); + Test("c:or_i(and_v(v:older(16),pk_h(02d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e)),pk_h(026a245bf6dc698504c89a20cfded60853152b695336c28063b61c65cbd269e6b4))", "6360b26976a9149fc5dbe5efdce10374a4dd4053c93af540211718886776a9142fbd32c8dd59ee7c17e66cb6ebea7e9846c3040f8868ac", "6360b26976a9144d4421361c3289bdad06441ffaee8be8e786f1ad886776a91460d4a7bcbd08f58e58bd208d1069837d7adb16ae8868ac", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG, 12, 3, 2 + 34 + 73, 2 + 33 + 66, 4); + Test("or_d(c:pk_h(02e493dbf1c10d80f3581e4904930b1404cc6c13900ee0758474fa94abe8c4cd13),andor(c:pk_k(024ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c97),older(2016),after(1567547623)))", "76a914c42e7ef92fdb603af844d064faad95db9bcdfd3d88ac736421024ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c97ac6404e7e06e5db16702e007b26868", "76a91421ab1a140d0d305b8ff62bdb887d9fef82c9899e88ac7364204ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c97ac6404e7e06e5db16702e007b26868", TESTMODE_VALID | TESTMODE_NONMAL, 13, 3, 1 + 34 + 73, 1 + 33 + 66, 5); + Test("c:andor(ripemd160(6ad07d21fd5dfc646f0b30577045ce201616b9ba),pk_h(02d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e),and_v(v:hash256(8a35d9ca92a48eaade6f53a64985e9e2afeb74dcf8acb4c3721e0dc7e4294b25),pk_h(03d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a)))", "82012088a6146ad07d21fd5dfc646f0b30577045ce201616b9ba876482012088aa208a35d9ca92a48eaade6f53a64985e9e2afeb74dcf8acb4c3721e0dc7e4294b258876a914dd100be7d9aea5721158ebde6d6a1fd8fff93bb1886776a9149fc5dbe5efdce10374a4dd4053c93af5402117188868ac", "82012088a6146ad07d21fd5dfc646f0b30577045ce201616b9ba876482012088aa208a35d9ca92a48eaade6f53a64985e9e2afeb74dcf8acb4c3721e0dc7e4294b258876a914a63d1e4d2ed109246c600ec8c19cce546b65b1cc886776a9144d4421361c3289bdad06441ffaee8be8e786f1ad8868ac", TESTMODE_VALID | TESTMODE_NEEDSIG, 18, 3, 33 + 34 + 73, 33 + 33 + 66, 5); + Test("c:andor(u:ripemd160(6ad07d21fd5dfc646f0b30577045ce201616b9ba),pk_h(03daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729),or_i(pk_h(022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01),pk_h(0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798)))", "6382012088a6146ad07d21fd5dfc646f0b30577045ce201616b9ba87670068646376a9149652d86bedf43ad264362e6e6eba6eb764508127886776a914751e76e8199196d454941c45d1b3a323f1433bd688686776a91420d637c1a6404d2227f3561fdbaff5a680dba6488868ac", "6382012088a6146ad07d21fd5dfc646f0b30577045ce201616b9ba87670068646376a914ceedcb44b38bdbcb614d872223964fd3dca8a434886776a914f678d9b79045452c8c64e9309d0f0046056e26c588686776a914a2a75e1819afa208f6c89ae0da43021116dfcb0c8868ac", TESTMODE_VALID | TESTMODE_NEEDSIG, 23, 4, 2 + 33 + 34 + 73, 2 + 33 + 33 + 66, 5); + Test("c:or_i(andor(c:pk_h(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),pk_h(022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01),pk_h(02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5)),pk_k(02d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e))", "6376a914fcd35ddacad9f2d5be5e464639441c6065e6955d88ac6476a91406afd46bcdfd22ef94ac122aa11f241244a37ecc886776a9149652d86bedf43ad264362e6e6eba6eb7645081278868672102d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e68ac", "6376a914fd1690c37fa3b0f04395ddc9415b220ab1ccc59588ac6476a9149b652a14674a506079f574d20ca7daef6f9a66bb886776a914ceedcb44b38bdbcb614d872223964fd3dca8a43488686720d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e68ac", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG, 17, 5, 2 + 34 + 73 + 34 + 73, 2 + 33 + 66 + 33 + 66, 6); + Test("thresh(1,c:pk_k(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),altv:after(1000000000),altv:after(100))", "2103d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65ac6b6300670400ca9a3bb16951686c936b6300670164b16951686c935187", "20d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65ac6b6300670400ca9a3bb16951686c936b6300670164b16951686c935187", TESTMODE_VALID, 18, 3, 73 + 2 + 2, 66 + 2 + 2, 4); + Test("thresh(2,c:pk_k(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),ac:pk_k(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556),altv:after(1000000000),altv:after(100))", "2103d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65ac6b2103fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556ac6c936b6300670400ca9a3bb16951686c936b6300670164b16951686c935287", "20d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65ac6b20fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556ac6c936b6300670400ca9a3bb16951686c936b6300670164b16951686c935287", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_TIMELOCKMIX, 22, 4, 73 + 73 + 2 + 2, 66 + 66 + 2 + 2, 5); + + // Additional Tapscript-related tests + // Edge cases when parsing multi_a from script: + // - no pubkey at all + // - no pubkey before a CHECKSIGADD + // - no pubkey before the CHECKSIG + constexpr KeyConverter tap_converter{miniscript::MiniscriptContext::TAPSCRIPT}; + constexpr KeyConverter wsh_converter{miniscript::MiniscriptContext::P2WSH}; + const auto no_pubkey{ParseHex("ac519c")}; + BOOST_CHECK(miniscript::FromScript({no_pubkey.begin(), no_pubkey.end()}, tap_converter) == nullptr); + const auto incomplete_multi_a{ParseHex("ba20c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5ba519c")}; + BOOST_CHECK(miniscript::FromScript({incomplete_multi_a.begin(), incomplete_multi_a.end()}, tap_converter) == nullptr); + const auto incomplete_multi_a_2{ParseHex("ac2079be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ac20c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5ba519c")}; + BOOST_CHECK(miniscript::FromScript({incomplete_multi_a_2.begin(), incomplete_multi_a_2.end()}, tap_converter) == nullptr); + // Can use multi_a under Tapscript but not P2WSH. + Test("and_v(v:multi_a(2,03d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a,025601570cb47f238d2b0286db4a990fa0f3ba28d1a319f5e7cf55c2a2444da7cc),after(1231488000))", "?", "20d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85aac205601570cb47f238d2b0286db4a990fa0f3ba28d1a319f5e7cf55c2a2444da7ccba529d0400046749b1", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG | TESTMODE_P2WSH_INVALID, 4, 2, {}, {}, 3); + // Can use more than 20 keys in a multi_a. + std::string ms_str_multi_a{"multi_a(1,"}; + for (size_t i = 0; i < 21; ++i) { + ms_str_multi_a += HexStr(g_testdata->pubkeys[i]); + if (i < 20) ms_str_multi_a += ","; + } + ms_str_multi_a += ")"; + Test(ms_str_multi_a, "?", "2079be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ac20c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5ba20f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9ba20e493dbf1c10d80f3581e4904930b1404cc6c13900ee0758474fa94abe8c4cd13ba202f8bde4d1a07209355b4a7250a5c5128e88b84bddc619ab7cba8d569b240efe4ba20fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556ba205cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bcba202f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01ba20acd484e2f0c7f65309ad178a9f559abde09796974c57e714c35f110dfc27ccbeba20a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c7ba20774ae7f858a9411e5ef4246b70c65aac5649980be5c17891bbec17895da008cbba20d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85aba20f28773c2d975288bc7d1d205c3748651b075fbc6610e58cddeeddf8f19405aa8ba20499fdf9e895e719cfd64e67f07d38e3226aa7b63678949e6e49b241a60e823e4ba20d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080eba20e60fce93b59e9ec53011aabc21c23e97b2a31369b87a5ae9c44ee89e2a6dec0aba20defdea4cdb677750a420fee807eacf21eb9898ae79b9768766e4faa04a2d4a34ba205601570cb47f238d2b0286db4a990fa0f3ba28d1a319f5e7cf55c2a2444da7ccba202b4ea0a797a443d293ef5cff444f4979f06acfebd7e86d277475656138385b6cba204ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c97ba20352bbf4a4cdd12564f93fa332ce333301d9ad40271f8107181340aef25be59d5ba519c", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG | TESTMODE_P2WSH_INVALID, 22, 21, {}, {}, 22); + // Since 'd:' is 'u' we can use it directly inside a thresh. But we can't under P2WSH. + Test("thresh(2,dv:older(42),s:pk(025cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bc),s:pk(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65))", "?", "7663012ab269687c205cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bcac937c20d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65ac935287", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG | TESTMODE_P2WSH_INVALID, 12, 3, {}, {}, 4); + // We can have a script that has more than 201 ops (n = 99), that needs a stack size > 100 (n = 110), or has a + // script that is larger than 3600 bytes (n = 200). All that can't be under P2WSH. + for (const auto pk_count: {99, 110, 200}) { + std::string ms_str_large; + for (auto i = 0; i < pk_count - 1; ++i) { + ms_str_large += "and_b(pk(" + HexStr(g_testdata->pubkeys[i]) + "),a:"; + } + ms_str_large += "pk(" + HexStr(g_testdata->pubkeys[pk_count - 1]) + ")"; + ms_str_large.insert(ms_str_large.end(), pk_count - 1, ')'); + Test(ms_str_large, "?", "?", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG | TESTMODE_P2WSH_INVALID, pk_count + (pk_count - 1) * 3, pk_count, {}, {}, pk_count + 1); + } + // We can have a script that reaches a stack size of 1000 during execution. + std::string ms_stack_limit; + auto count{998}; + for (auto i = 0; i < count; ++i) { + ms_stack_limit += "and_b(older(1),a:"; + } + ms_stack_limit += "pk(" + HexStr(g_testdata->pubkeys[0]) + ")"; + ms_stack_limit.insert(ms_stack_limit.end(), count, ')'); + const auto ms_stack_ok{miniscript::FromString(ms_stack_limit, tap_converter)}; + BOOST_CHECK(ms_stack_ok && ms_stack_ok->CheckStackSize()); + Test(ms_stack_limit, "?", "?", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG | TESTMODE_P2WSH_INVALID, 4 * count + 1, 1, {}, {}, 1 + count + 1); + // But one more element on the stack during execution will make it fail. And we'd detect that. + count++; + ms_stack_limit = "and_b(older(1),a:" + ms_stack_limit + ")"; + const auto ms_stack_nok{miniscript::FromString(ms_stack_limit, tap_converter)}; + BOOST_CHECK(ms_stack_nok && !ms_stack_nok->CheckStackSize()); + Test(ms_stack_limit, "?", "?", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG | TESTMODE_P2WSH_INVALID, 4 * count + 1, 1, {}, {}, 1 + count + 1); // Misc unit tests // A Script with a non minimal push is invalid std::vector<unsigned char> nonminpush = ParseHex("0000210232780000feff00ffffffffffff21ff005f00ae21ae00000000060602060406564c2102320000060900fe00005f00ae21ae00100000060606060606000000000000000000000000000000000000000000000000000000000000000000"); const CScript nonminpush_script(nonminpush.begin(), nonminpush.end()); - BOOST_CHECK(miniscript::FromScript(nonminpush_script, CONVERTER) == nullptr); + BOOST_CHECK(miniscript::FromScript(nonminpush_script, wsh_converter) == nullptr); + BOOST_CHECK(miniscript::FromScript(nonminpush_script, tap_converter) == nullptr); // A non-minimal VERIFY (<key> CHECKSIG VERIFY 1) std::vector<unsigned char> nonminverify = ParseHex("2103a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c7ac6951"); const CScript nonminverify_script(nonminverify.begin(), nonminverify.end()); - BOOST_CHECK(miniscript::FromScript(nonminverify_script, CONVERTER) == nullptr); + BOOST_CHECK(miniscript::FromScript(nonminverify_script, wsh_converter) == nullptr); + BOOST_CHECK(miniscript::FromScript(nonminverify_script, tap_converter) == nullptr); // A threshold as large as the number of subs is valid. - Test("thresh(2,c:pk_k(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),altv:after(100))", "2103d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65ac6b6300670164b16951686c935287", TESTMODE_VALID | TESTMODE_NEEDSIG | TESTMODE_NONMAL); + Test("thresh(2,c:pk_k(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),altv:after(100))", "2103d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65ac6b6300670164b16951686c935287", "20d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65ac6b6300670164b16951686c935287", TESTMODE_VALID | TESTMODE_NEEDSIG | TESTMODE_NONMAL); // A threshold of 1 is valid. - Test("thresh(1,c:pk_k(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),sc:pk_k(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556))", "2103d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65ac7c2103fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556ac935187", TESTMODE_VALID | TESTMODE_NEEDSIG | TESTMODE_NONMAL); + Test("thresh(1,c:pk_k(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),sc:pk_k(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556))", "2103d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65ac7c2103fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556ac935187", "20d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65ac7c20fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556ac935187", TESTMODE_VALID | TESTMODE_NEEDSIG | TESTMODE_NONMAL); // A threshold with a k larger than the number of subs is invalid - Test("thresh(3,c:pk_k(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),sc:pk_k(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556))", "2103d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65ac7c2103fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556ac935187", TESTMODE_INVALID); + Test("thresh(3,c:pk_k(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),sc:pk_k(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556))", "2103d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65ac7c2103fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556ac935187", "=", TESTMODE_INVALID); // A threshold with a k null is invalid - Test("thresh(0,c:pk_k(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),sc:pk_k(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556))", "2103d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65ac7c2103fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556ac935187", TESTMODE_INVALID); + Test("thresh(0,c:pk_k(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),sc:pk_k(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556))", "2103d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65ac7c2103fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556ac935187", "=", TESTMODE_INVALID); // For CHECKMULTISIG the OP cost is the number of keys, but the stack size is the number of sigs (+1) - const auto ms_multi = miniscript::FromString("multi(1,03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65,03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556,0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798)", CONVERTER); + const auto ms_multi = miniscript::FromString("multi(1,03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65,03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556,0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798)", wsh_converter); BOOST_CHECK(ms_multi); BOOST_CHECK_EQUAL(*ms_multi->GetOps(), 4); // 3 pubkeys + CMS BOOST_CHECK_EQUAL(*ms_multi->GetStackSize(), 2); // 1 sig + dummy elem @@ -509,47 +673,46 @@ BOOST_AUTO_TEST_CASE(fixed_tests) // Unfortunately, this rule is consensus for Taproot but only policy for P2WSH. Therefore we can't // (for now) have 'd:' be 'u'. This tests we can't use a 'd:' wrapper for a thresh, which requires // its subs to all be 'u' (taken from https://github.com/rust-bitcoin/rust-miniscript/discussions/341). - const auto ms_minimalif = miniscript::FromString("thresh(3,c:pk_k(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),sc:pk_k(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556),sc:pk_k(0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798),sdv:older(32))", CONVERTER); + const auto ms_minimalif = miniscript::FromString("thresh(3,c:pk_k(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),sc:pk_k(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556),sc:pk_k(0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798),sdv:older(32))", wsh_converter); BOOST_CHECK(ms_minimalif && !ms_minimalif->IsValid()); // A Miniscript with duplicate keys is not sane - const auto ms_dup1 = miniscript::FromString("and_v(v:pk(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),pk(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65))", CONVERTER); + const auto ms_dup1 = miniscript::FromString("and_v(v:pk(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),pk(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65))", wsh_converter); BOOST_CHECK(ms_dup1); BOOST_CHECK(!ms_dup1->IsSane() && !ms_dup1->CheckDuplicateKey()); // Same with a disjunction, and different key nodes (pk and pkh) - const auto ms_dup2 = miniscript::FromString("or_b(c:pk_k(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),ac:pk_h(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65))", CONVERTER); + const auto ms_dup2 = miniscript::FromString("or_b(c:pk_k(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),ac:pk_h(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65))", wsh_converter); BOOST_CHECK(ms_dup2 && !ms_dup2->IsSane() && !ms_dup2->CheckDuplicateKey()); // Same when the duplicates are leaves or a larger tree - const auto ms_dup3 = miniscript::FromString("or_i(and_b(pk(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),s:pk(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556)),and_b(older(1),s:pk(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65)))", CONVERTER); + const auto ms_dup3 = miniscript::FromString("or_i(and_b(pk(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),s:pk(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556)),and_b(older(1),s:pk(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65)))", wsh_converter); BOOST_CHECK(ms_dup3 && !ms_dup3->IsSane() && !ms_dup3->CheckDuplicateKey()); // Same when the duplicates are on different levels in the tree - const auto ms_dup4 = miniscript::FromString("thresh(2,pkh(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),s:pk(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556),a:and_b(dv:older(1),s:pk(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65)))", CONVERTER); + const auto ms_dup4 = miniscript::FromString("thresh(2,pkh(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),s:pk(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556),a:and_b(dv:older(1),s:pk(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65)))", wsh_converter); BOOST_CHECK(ms_dup4 && !ms_dup4->IsSane() && !ms_dup4->CheckDuplicateKey()); // Sanity check the opposite is true, too. An otherwise sane Miniscript with no duplicate keys is sane. - const auto ms_nondup = miniscript::FromString("pk(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65)", CONVERTER); + const auto ms_nondup = miniscript::FromString("pk(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65)", wsh_converter); BOOST_CHECK(ms_nondup && ms_nondup->CheckDuplicateKey() && ms_nondup->IsSane()); // Test we find the first insane sub closer to be a leaf node. This fragment is insane for two reasons: // 1. It can be spent without a signature // 2. It contains timelock mixes // We'll report the timelock mix error, as it's "deeper" (closer to be a leaf node) than the "no 's' property" // error is. - const auto ms_ins = miniscript::FromString("or_i(and_b(after(1),a:after(1000000000)),pk(03cdabb7f2dce7bfbd8a0b9570c6fd1e712e5d64045e9d6b517b3d5072251dc204))", CONVERTER); + const auto ms_ins = miniscript::FromString("or_i(and_b(after(1),a:after(1000000000)),pk(03cdabb7f2dce7bfbd8a0b9570c6fd1e712e5d64045e9d6b517b3d5072251dc204))", wsh_converter); BOOST_CHECK(ms_ins && ms_ins->IsValid() && !ms_ins->IsSane()); const auto insane_sub = ms_ins->FindInsaneSub(); - BOOST_CHECK(insane_sub && *insane_sub->ToString(CONVERTER) == "and_b(after(1),a:after(1000000000))"); + BOOST_CHECK(insane_sub && *insane_sub->ToString(wsh_converter) == "and_b(after(1),a:after(1000000000))"); // Timelock tests - Test("after(100)", "?", TESTMODE_VALID | TESTMODE_NONMAL); // only heightlock - Test("after(1000000000)", "?", TESTMODE_VALID | TESTMODE_NONMAL); // only timelock - Test("or_b(l:after(100),al:after(1000000000))", "?", TESTMODE_VALID); // or_b(timelock, heighlock) valid - Test("and_b(after(100),a:after(1000000000))", "?", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_TIMELOCKMIX); // and_b(timelock, heighlock) invalid + Test("after(100)", "?", "?", TESTMODE_VALID | TESTMODE_NONMAL); // only heightlock + Test("after(1000000000)", "?", "?", TESTMODE_VALID | TESTMODE_NONMAL); // only timelock + Test("or_b(l:after(100),al:after(1000000000))", "?", "?", TESTMODE_VALID); // or_b(timelock, heighlock) valid + Test("and_b(after(100),a:after(1000000000))", "?", "?", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_TIMELOCKMIX); // and_b(timelock, heighlock) invalid /* This is correctly detected as non-malleable but for the wrong reason. The type system assumes that branches 1 and 2 can be spent together to create a non-malleble witness, but because of mixing of timelocks they cannot be spent together. But since exactly one of the two after's can be satisfied, the witness involving the key cannot be malleated. */ - Test("thresh(2,ltv:after(1000000000),altv:after(100),a:pk(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65))", "?", TESTMODE_VALID | TESTMODE_TIMELOCKMIX | TESTMODE_NONMAL); // thresh with k = 2 + Test("thresh(2,ltv:after(1000000000),altv:after(100),a:pk(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65))", "?", "?", TESTMODE_VALID | TESTMODE_TIMELOCKMIX | TESTMODE_NONMAL); // thresh with k = 2 // This is actually non-malleable in practice, but we cannot detect it in type system. See above rationale - Test("thresh(1,c:pk_k(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),altv:after(1000000000),altv:after(100))", "?", TESTMODE_VALID); // thresh with k = 1 - + Test("thresh(1,c:pk_k(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),altv:after(1000000000),altv:after(100))", "?", "?", TESTMODE_VALID); // thresh with k = 1 g_testdata.reset(); } diff --git a/src/test/net_tests.cpp b/src/test/net_tests.cpp index 5976aa3713..7c98c382e4 100644 --- a/src/test/net_tests.cpp +++ b/src/test/net_tests.cpp @@ -718,47 +718,55 @@ BOOST_AUTO_TEST_CASE(get_local_addr_for_peer_port) BOOST_AUTO_TEST_CASE(LimitedAndReachable_Network) { - BOOST_CHECK(IsReachable(NET_IPV4)); - BOOST_CHECK(IsReachable(NET_IPV6)); - BOOST_CHECK(IsReachable(NET_ONION)); - BOOST_CHECK(IsReachable(NET_I2P)); - BOOST_CHECK(IsReachable(NET_CJDNS)); - - SetReachable(NET_IPV4, false); - SetReachable(NET_IPV6, false); - SetReachable(NET_ONION, false); - SetReachable(NET_I2P, false); - SetReachable(NET_CJDNS, false); - - BOOST_CHECK(!IsReachable(NET_IPV4)); - BOOST_CHECK(!IsReachable(NET_IPV6)); - BOOST_CHECK(!IsReachable(NET_ONION)); - BOOST_CHECK(!IsReachable(NET_I2P)); - BOOST_CHECK(!IsReachable(NET_CJDNS)); - - SetReachable(NET_IPV4, true); - SetReachable(NET_IPV6, true); - SetReachable(NET_ONION, true); - SetReachable(NET_I2P, true); - SetReachable(NET_CJDNS, true); - - BOOST_CHECK(IsReachable(NET_IPV4)); - BOOST_CHECK(IsReachable(NET_IPV6)); - BOOST_CHECK(IsReachable(NET_ONION)); - BOOST_CHECK(IsReachable(NET_I2P)); - BOOST_CHECK(IsReachable(NET_CJDNS)); + BOOST_CHECK(g_reachable_nets.Contains(NET_IPV4)); + BOOST_CHECK(g_reachable_nets.Contains(NET_IPV6)); + BOOST_CHECK(g_reachable_nets.Contains(NET_ONION)); + BOOST_CHECK(g_reachable_nets.Contains(NET_I2P)); + BOOST_CHECK(g_reachable_nets.Contains(NET_CJDNS)); + + g_reachable_nets.Remove(NET_IPV4); + g_reachable_nets.Remove(NET_IPV6); + g_reachable_nets.Remove(NET_ONION); + g_reachable_nets.Remove(NET_I2P); + g_reachable_nets.Remove(NET_CJDNS); + + BOOST_CHECK(!g_reachable_nets.Contains(NET_IPV4)); + BOOST_CHECK(!g_reachable_nets.Contains(NET_IPV6)); + BOOST_CHECK(!g_reachable_nets.Contains(NET_ONION)); + BOOST_CHECK(!g_reachable_nets.Contains(NET_I2P)); + BOOST_CHECK(!g_reachable_nets.Contains(NET_CJDNS)); + + g_reachable_nets.Add(NET_IPV4); + g_reachable_nets.Add(NET_IPV6); + g_reachable_nets.Add(NET_ONION); + g_reachable_nets.Add(NET_I2P); + g_reachable_nets.Add(NET_CJDNS); + + BOOST_CHECK(g_reachable_nets.Contains(NET_IPV4)); + BOOST_CHECK(g_reachable_nets.Contains(NET_IPV6)); + BOOST_CHECK(g_reachable_nets.Contains(NET_ONION)); + BOOST_CHECK(g_reachable_nets.Contains(NET_I2P)); + BOOST_CHECK(g_reachable_nets.Contains(NET_CJDNS)); } BOOST_AUTO_TEST_CASE(LimitedAndReachable_NetworkCaseUnroutableAndInternal) { - BOOST_CHECK(IsReachable(NET_UNROUTABLE)); - BOOST_CHECK(IsReachable(NET_INTERNAL)); - - SetReachable(NET_UNROUTABLE, false); - SetReachable(NET_INTERNAL, false); - - BOOST_CHECK(IsReachable(NET_UNROUTABLE)); // Ignored for both networks - BOOST_CHECK(IsReachable(NET_INTERNAL)); + // Should be reachable by default. + BOOST_CHECK(g_reachable_nets.Contains(NET_UNROUTABLE)); + BOOST_CHECK(g_reachable_nets.Contains(NET_INTERNAL)); + + g_reachable_nets.RemoveAll(); + + BOOST_CHECK(!g_reachable_nets.Contains(NET_UNROUTABLE)); + BOOST_CHECK(!g_reachable_nets.Contains(NET_INTERNAL)); + + g_reachable_nets.Add(NET_IPV4); + g_reachable_nets.Add(NET_IPV6); + g_reachable_nets.Add(NET_ONION); + g_reachable_nets.Add(NET_I2P); + g_reachable_nets.Add(NET_CJDNS); + g_reachable_nets.Add(NET_UNROUTABLE); + g_reachable_nets.Add(NET_INTERNAL); } CNetAddr UtilBuildAddress(unsigned char p1, unsigned char p2, unsigned char p3, unsigned char p4) @@ -776,13 +784,13 @@ BOOST_AUTO_TEST_CASE(LimitedAndReachable_CNetAddr) { CNetAddr addr = UtilBuildAddress(0x001, 0x001, 0x001, 0x001); // 1.1.1.1 - SetReachable(NET_IPV4, true); - BOOST_CHECK(IsReachable(addr)); + g_reachable_nets.Add(NET_IPV4); + BOOST_CHECK(g_reachable_nets.Contains(addr)); - SetReachable(NET_IPV4, false); - BOOST_CHECK(!IsReachable(addr)); + g_reachable_nets.Remove(NET_IPV4); + BOOST_CHECK(!g_reachable_nets.Contains(addr)); - SetReachable(NET_IPV4, true); // have to reset this, because this is stateful. + g_reachable_nets.Add(NET_IPV4); // have to reset this, because this is stateful. } @@ -790,7 +798,7 @@ BOOST_AUTO_TEST_CASE(LocalAddress_BasicLifecycle) { CService addr = CService(UtilBuildAddress(0x002, 0x001, 0x001, 0x001), 1000); // 2.1.1.1:1000 - SetReachable(NET_IPV4, true); + g_reachable_nets.Add(NET_IPV4); BOOST_CHECK(!IsLocal(addr)); BOOST_CHECK(AddLocal(addr, 1000)); @@ -915,7 +923,7 @@ BOOST_AUTO_TEST_CASE(advertise_local_address) ConnectionType::OUTBOUND_FULL_RELAY, /*inbound_onion=*/false); }; - SetReachable(NET_CJDNS, true); + g_reachable_nets.Add(NET_CJDNS); CAddress addr_ipv4{Lookup("1.2.3.4", 8333, false).value(), NODE_NONE}; BOOST_REQUIRE(addr_ipv4.IsValid()); @@ -1080,9 +1088,9 @@ public: bool reject{false}; auto msg = m_transport.GetReceivedMessage({}, reject); if (reject) { - ret.push_back(std::nullopt); + ret.emplace_back(std::nullopt); } else { - ret.push_back(std::move(msg)); + ret.emplace_back(std::move(msg)); } progress = true; } diff --git a/src/test/policy_fee_tests.cpp b/src/test/policy_fee_tests.cpp index 25fb5343e3..29d70cb5f8 100644 --- a/src/test/policy_fee_tests.cpp +++ b/src/test/policy_fee_tests.cpp @@ -13,7 +13,8 @@ BOOST_AUTO_TEST_SUITE(policy_fee_tests) BOOST_AUTO_TEST_CASE(FeeRounder) { - FeeFilterRounder fee_rounder{CFeeRate{1000}}; + FastRandomContext rng{/*fDeterministic=*/true}; + FeeFilterRounder fee_rounder{CFeeRate{1000}, rng}; // check that 1000 rounds to 974 or 1071 std::set<CAmount> results; diff --git a/src/test/rbf_tests.cpp b/src/test/rbf_tests.cpp index 10205cd641..fb6a3614c0 100644 --- a/src/test/rbf_tests.cpp +++ b/src/test/rbf_tests.cpp @@ -27,7 +27,7 @@ static inline CTransactionRef make_tx(const std::vector<CTransactionRef>& inputs tx.vin[i].prevout.n = 0; // Add a witness so wtxid != txid CScriptWitness witness; - witness.stack.push_back(std::vector<unsigned char>(i + 10)); + witness.stack.emplace_back(i + 10); tx.vin[i].scriptWitness = witness; } for (size_t i = 0; i < output_values.size(); ++i) { diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp index 4cad3ec68e..0d2460c606 100644 --- a/src/test/rpc_tests.cpp +++ b/src/test/rpc_tests.cpp @@ -428,11 +428,11 @@ BOOST_AUTO_TEST_CASE(rpc_getblockstats_calculate_percentiles_by_weight) CAmount result[NUM_GETBLOCKSTATS_PERCENTILES] = { 0 }; for (int64_t i = 0; i < 100; i++) { - feerates.emplace_back(std::make_pair(1 ,1)); + feerates.emplace_back(1 ,1); } for (int64_t i = 0; i < 100; i++) { - feerates.emplace_back(std::make_pair(2 ,1)); + feerates.emplace_back(2 ,1); } CalculatePercentilesByWeight(result, feerates, total_weight); @@ -447,11 +447,11 @@ BOOST_AUTO_TEST_CASE(rpc_getblockstats_calculate_percentiles_by_weight) CAmount result2[NUM_GETBLOCKSTATS_PERCENTILES] = { 0 }; feerates.clear(); - feerates.emplace_back(std::make_pair(1, 9)); - feerates.emplace_back(std::make_pair(2 , 16)); //10th + 25th percentile - feerates.emplace_back(std::make_pair(4 ,50)); //50th + 75th percentile - feerates.emplace_back(std::make_pair(5 ,10)); - feerates.emplace_back(std::make_pair(9 ,15)); // 90th percentile + feerates.emplace_back(1, 9); + feerates.emplace_back(2 , 16); //10th + 25th percentile + feerates.emplace_back(4 ,50); //50th + 75th percentile + feerates.emplace_back(5 ,10); + feerates.emplace_back(9 ,15); // 90th percentile CalculatePercentilesByWeight(result2, feerates, total_weight); @@ -466,12 +466,12 @@ BOOST_AUTO_TEST_CASE(rpc_getblockstats_calculate_percentiles_by_weight) CAmount result3[NUM_GETBLOCKSTATS_PERCENTILES] = { 0 }; feerates.clear(); - feerates.emplace_back(std::make_pair(1, 9)); - feerates.emplace_back(std::make_pair(2 , 11)); // 10th percentile - feerates.emplace_back(std::make_pair(2 , 5)); // 25th percentile - feerates.emplace_back(std::make_pair(4 ,50)); //50th + 75th percentile - feerates.emplace_back(std::make_pair(5 ,10)); - feerates.emplace_back(std::make_pair(9 ,15)); // 90th percentile + feerates.emplace_back(1, 9); + feerates.emplace_back(2 , 11); // 10th percentile + feerates.emplace_back(2 , 5); // 25th percentile + feerates.emplace_back(4 ,50); //50th + 75th percentile + feerates.emplace_back(5 ,10); + feerates.emplace_back(9 ,15); // 90th percentile CalculatePercentilesByWeight(result3, feerates, total_weight); @@ -486,11 +486,11 @@ BOOST_AUTO_TEST_CASE(rpc_getblockstats_calculate_percentiles_by_weight) CAmount result4[NUM_GETBLOCKSTATS_PERCENTILES] = { 0 }; feerates.clear(); - feerates.emplace_back(std::make_pair(1, 100)); - feerates.emplace_back(std::make_pair(2, 1)); - feerates.emplace_back(std::make_pair(3, 1)); - feerates.emplace_back(std::make_pair(3, 1)); - feerates.emplace_back(std::make_pair(999999, 1)); + feerates.emplace_back(1, 100); + feerates.emplace_back(2, 1); + feerates.emplace_back(3, 1); + feerates.emplace_back(3, 1); + feerates.emplace_back(999999, 1); CalculatePercentilesByWeight(result4, feerates, total_weight); @@ -506,7 +506,7 @@ BOOST_AUTO_TEST_CASE(check_dup_param_names) auto make_rpc = [](std::vector<std::tuple<std::string, ParamType>> param_names) { std::vector<RPCArg> params; std::vector<RPCArg> options; - auto push_options = [&] { if (!options.empty()) params.emplace_back(RPCArg{strprintf("options%i", params.size()), RPCArg::Type::OBJ_NAMED_PARAMS, RPCArg::Optional::OMITTED, "", std::move(options)}); }; + auto push_options = [&] { if (!options.empty()) params.emplace_back(strprintf("options%i", params.size()), RPCArg::Type::OBJ_NAMED_PARAMS, RPCArg::Optional::OMITTED, "", std::move(options)); }; for (auto& [param_name, param_type] : param_names) { if (param_type == POSITIONAL) { push_options(); diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp index 94656b229e..624d0b2c12 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -1637,6 +1637,37 @@ BOOST_AUTO_TEST_CASE(bitcoinconsensus_verify_script_invalid_flags) BOOST_CHECK_EQUAL(err, bitcoinconsensus_ERR_INVALID_FLAGS); } +/* Test bitcoinconsensus_verify_script returns spent outputs required err */ +BOOST_AUTO_TEST_CASE(bitcoinconsensus_verify_script_spent_outputs_required_err) +{ + unsigned int libconsensus_flags{bitcoinconsensus_SCRIPT_FLAGS_VERIFY_TAPROOT}; + const int nIn{0}; + + CScript scriptPubKey; + CScript scriptSig; + CScriptWitness wit; + + scriptPubKey << OP_EQUAL; + CTransaction creditTx{BuildCreditingTransaction(scriptPubKey, 1)}; + CTransaction spendTx{BuildSpendingTransaction(scriptSig, wit, creditTx)}; + + CDataStream stream(SER_NETWORK, PROTOCOL_VERSION); + stream << spendTx; + + bitcoinconsensus_error err; + int result{bitcoinconsensus_verify_script_with_spent_outputs(scriptPubKey.data(), scriptPubKey.size(), creditTx.vout[0].nValue, UCharCast(stream.data()), stream.size(), nullptr, 0, nIn, libconsensus_flags, &err)}; + BOOST_CHECK_EQUAL(result, 0); + BOOST_CHECK_EQUAL(err, bitcoinconsensus_ERR_SPENT_OUTPUTS_REQUIRED); + + result = bitcoinconsensus_verify_script_with_amount(scriptPubKey.data(), scriptPubKey.size(), creditTx.vout[0].nValue, UCharCast(stream.data()), stream.size(), nIn, libconsensus_flags, &err); + BOOST_CHECK_EQUAL(result, 0); + BOOST_CHECK_EQUAL(err, bitcoinconsensus_ERR_SPENT_OUTPUTS_REQUIRED); + + result = bitcoinconsensus_verify_script(scriptPubKey.data(), scriptPubKey.size(), UCharCast(stream.data()), stream.size(), nIn, libconsensus_flags, &err); + BOOST_CHECK_EQUAL(result, 0); + BOOST_CHECK_EQUAL(err, bitcoinconsensus_ERR_SPENT_OUTPUTS_REQUIRED); +} + #endif // defined(HAVE_CONSENSUS_LIB) static std::vector<unsigned int> AllConsensusFlags() @@ -1685,12 +1716,29 @@ static void AssetTest(const UniValue& test) PrecomputedTransactionData txdata; txdata.Init(tx, std::vector<CTxOut>(prevouts)); CachingTransactionSignatureChecker txcheck(&tx, idx, prevouts[idx].nValue, true, txdata); + +#if defined(HAVE_CONSENSUS_LIB) + CDataStream stream(SER_NETWORK, PROTOCOL_VERSION); + stream << tx; + std::vector<UTXO> utxos; + utxos.resize(prevouts.size()); + for (size_t i = 0; i < prevouts.size(); i++) { + utxos[i].scriptPubKey = prevouts[i].scriptPubKey.data(); + utxos[i].scriptPubKeySize = prevouts[i].scriptPubKey.size(); + utxos[i].value = prevouts[i].nValue; + } +#endif + for (const auto flags : ALL_CONSENSUS_FLAGS) { // "final": true tests are valid for all flags. Others are only valid with flags that are // a subset of test_flags. if (fin || ((flags & test_flags) == flags)) { bool ret = VerifyScript(tx.vin[idx].scriptSig, prevouts[idx].scriptPubKey, &tx.vin[idx].scriptWitness, flags, txcheck, nullptr); BOOST_CHECK(ret); +#if defined(HAVE_CONSENSUS_LIB) + int lib_ret = bitcoinconsensus_verify_script_with_spent_outputs(prevouts[idx].scriptPubKey.data(), prevouts[idx].scriptPubKey.size(), prevouts[idx].nValue, UCharCast(stream.data()), stream.size(), utxos.data(), utxos.size(), idx, flags, nullptr); + BOOST_CHECK(lib_ret == 1); +#endif } } } @@ -1702,11 +1750,28 @@ static void AssetTest(const UniValue& test) PrecomputedTransactionData txdata; txdata.Init(tx, std::vector<CTxOut>(prevouts)); CachingTransactionSignatureChecker txcheck(&tx, idx, prevouts[idx].nValue, true, txdata); + +#if defined(HAVE_CONSENSUS_LIB) + CDataStream stream(SER_NETWORK, PROTOCOL_VERSION); + stream << tx; + std::vector<UTXO> utxos; + utxos.resize(prevouts.size()); + for (size_t i = 0; i < prevouts.size(); i++) { + utxos[i].scriptPubKey = prevouts[i].scriptPubKey.data(); + utxos[i].scriptPubKeySize = prevouts[i].scriptPubKey.size(); + utxos[i].value = prevouts[i].nValue; + } +#endif + 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. if ((flags & test_flags) == test_flags) { bool ret = VerifyScript(tx.vin[idx].scriptSig, prevouts[idx].scriptPubKey, &tx.vin[idx].scriptWitness, flags, txcheck, nullptr); BOOST_CHECK(!ret); +#if defined(HAVE_CONSENSUS_LIB) + int lib_ret = bitcoinconsensus_verify_script_with_spent_outputs(prevouts[idx].scriptPubKey.data(), prevouts[idx].scriptPubKey.size(), prevouts[idx].nValue, UCharCast(stream.data()), stream.size(), utxos.data(), utxos.size(), idx, flags, nullptr); + BOOST_CHECK(lib_ret == 0); +#endif } } } diff --git a/src/test/settings_tests.cpp b/src/test/settings_tests.cpp index eb11df0497..fa8ceb8dd6 100644 --- a/src/test/settings_tests.cpp +++ b/src/test/settings_tests.cpp @@ -119,16 +119,16 @@ static void CheckValues(const common::Settings& settings, const std::string& sin BOOST_AUTO_TEST_CASE(Simple) { common::Settings settings; - settings.command_line_options["name"].push_back("val1"); - settings.command_line_options["name"].push_back("val2"); - settings.ro_config["section"]["name"].push_back(2); + settings.command_line_options["name"].emplace_back("val1"); + settings.command_line_options["name"].emplace_back("val2"); + settings.ro_config["section"]["name"].emplace_back(2); // The last given arg takes precedence when specified via commandline. CheckValues(settings, R"("val2")", R"(["val1","val2",2])"); common::Settings settings2; - settings2.ro_config["section"]["name"].push_back("val2"); - settings2.ro_config["section"]["name"].push_back("val3"); + settings2.ro_config["section"]["name"].emplace_back("val2"); + settings2.ro_config["section"]["name"].emplace_back("val3"); // The first given arg takes precedence when specified via config file. CheckValues(settings2, R"("val2")", R"(["val2","val3"])"); @@ -141,7 +141,7 @@ BOOST_AUTO_TEST_CASE(Simple) BOOST_AUTO_TEST_CASE(NullOverride) { common::Settings settings; - settings.command_line_options["name"].push_back("value"); + settings.command_line_options["name"].emplace_back("value"); BOOST_CHECK_EQUAL(R"("value")", GetSetting(settings, "section", "name", false, false, false).write().c_str()); settings.forced_settings["name"] = {}; BOOST_CHECK_EQUAL(R"(null)", GetSetting(settings, "section", "name", false, false, false).write().c_str()); @@ -202,11 +202,11 @@ BOOST_FIXTURE_TEST_CASE(Merge, MergeTestingSetup) std::vector<common::SettingsValue>& dest) { if (action == SET || action == SECTION_SET) { for (int i = 0; i < 2; ++i) { - dest.push_back(value_prefix + ToString(++value_suffix)); + dest.emplace_back(value_prefix + ToString(++value_suffix)); desc += " " + name_prefix + name + "=" + dest.back().get_str(); } } else if (action == NEGATE || action == SECTION_NEGATE) { - dest.push_back(false); + dest.emplace_back(false); desc += " " + name_prefix + "no" + name; } }; diff --git a/src/test/sighash_tests.cpp b/src/test/sighash_tests.cpp index 178b16772b..9653edd84b 100644 --- a/src/test/sighash_tests.cpp +++ b/src/test/sighash_tests.cpp @@ -100,7 +100,7 @@ void static RandomTransaction(CMutableTransaction& tx, bool fSingle) int ins = (InsecureRandBits(2)) + 1; int outs = fSingle ? ins : (InsecureRandBits(2)) + 1; for (int in = 0; in < ins; in++) { - tx.vin.push_back(CTxIn()); + tx.vin.emplace_back(); CTxIn &txin = tx.vin.back(); txin.prevout.hash = InsecureRand256(); txin.prevout.n = InsecureRandBits(2); @@ -108,7 +108,7 @@ void static RandomTransaction(CMutableTransaction& tx, bool fSingle) txin.nSequence = (InsecureRandBool()) ? InsecureRand32() : std::numeric_limits<uint32_t>::max(); } for (int out = 0; out < outs; out++) { - tx.vout.push_back(CTxOut()); + tx.vout.emplace_back(); CTxOut &txout = tx.vout.back(); txout.nValue = InsecureRandMoneyAmount(); RandomScript(txout.scriptPubKey); diff --git a/src/test/sigopcount_tests.cpp b/src/test/sigopcount_tests.cpp index ec9490d745..c0bed50e1d 100644 --- a/src/test/sigopcount_tests.cpp +++ b/src/test/sigopcount_tests.cpp @@ -160,8 +160,8 @@ BOOST_AUTO_TEST_CASE(GetTxSigOpCost) CScript scriptPubKey = GetScriptForDestination(WitnessV0KeyHash(pubkey)); CScript scriptSig = CScript(); CScriptWitness scriptWitness; - scriptWitness.stack.push_back(std::vector<unsigned char>(0)); - scriptWitness.stack.push_back(std::vector<unsigned char>(0)); + scriptWitness.stack.emplace_back(0); + scriptWitness.stack.emplace_back(0); BuildTxs(spendingTx, coins, creationTx, scriptPubKey, scriptSig, scriptWitness); @@ -189,8 +189,8 @@ BOOST_AUTO_TEST_CASE(GetTxSigOpCost) CScript scriptPubKey = GetScriptForDestination(ScriptHash(scriptSig)); scriptSig = CScript() << ToByteVector(scriptSig); CScriptWitness scriptWitness; - scriptWitness.stack.push_back(std::vector<unsigned char>(0)); - scriptWitness.stack.push_back(std::vector<unsigned char>(0)); + scriptWitness.stack.emplace_back(0); + scriptWitness.stack.emplace_back(0); BuildTxs(spendingTx, coins, creationTx, scriptPubKey, scriptSig, scriptWitness); assert(GetTransactionSigOpCost(CTransaction(spendingTx), coins, flags) == 1); @@ -203,9 +203,9 @@ BOOST_AUTO_TEST_CASE(GetTxSigOpCost) CScript scriptPubKey = GetScriptForDestination(WitnessV0ScriptHash(witnessScript)); CScript scriptSig = CScript(); CScriptWitness scriptWitness; - scriptWitness.stack.push_back(std::vector<unsigned char>(0)); - scriptWitness.stack.push_back(std::vector<unsigned char>(0)); - scriptWitness.stack.push_back(std::vector<unsigned char>(witnessScript.begin(), witnessScript.end())); + scriptWitness.stack.emplace_back(0); + scriptWitness.stack.emplace_back(0); + scriptWitness.stack.emplace_back(witnessScript.begin(), witnessScript.end()); BuildTxs(spendingTx, coins, creationTx, scriptPubKey, scriptSig, scriptWitness); assert(GetTransactionSigOpCost(CTransaction(spendingTx), coins, flags) == 2); @@ -220,9 +220,9 @@ BOOST_AUTO_TEST_CASE(GetTxSigOpCost) CScript scriptPubKey = GetScriptForDestination(ScriptHash(redeemScript)); CScript scriptSig = CScript() << ToByteVector(redeemScript); CScriptWitness scriptWitness; - scriptWitness.stack.push_back(std::vector<unsigned char>(0)); - scriptWitness.stack.push_back(std::vector<unsigned char>(0)); - scriptWitness.stack.push_back(std::vector<unsigned char>(witnessScript.begin(), witnessScript.end())); + scriptWitness.stack.emplace_back(0); + scriptWitness.stack.emplace_back(0); + scriptWitness.stack.emplace_back(witnessScript.begin(), witnessScript.end()); BuildTxs(spendingTx, coins, creationTx, scriptPubKey, scriptSig, scriptWitness); assert(GetTransactionSigOpCost(CTransaction(spendingTx), coins, flags) == 2); diff --git a/src/test/txpackage_tests.cpp b/src/test/txpackage_tests.cpp index 571b58156f..4d9a5ef7f3 100644 --- a/src/test/txpackage_tests.cpp +++ b/src/test/txpackage_tests.cpp @@ -162,6 +162,7 @@ BOOST_FIXTURE_TEST_CASE(noncontextual_package_tests, TestChain100Setup) BOOST_CHECK_EQUAL(state.GetResult(), PackageValidationResult::PCKG_POLICY); BOOST_CHECK_EQUAL(state.GetRejectReason(), "package-not-sorted"); BOOST_CHECK(IsChildWithParents({tx_parent, tx_child})); + BOOST_CHECK(IsChildWithParentsTree({tx_parent, tx_child})); } // 24 Parents and 1 Child @@ -172,9 +173,9 @@ BOOST_FIXTURE_TEST_CASE(noncontextual_package_tests, TestChain100Setup) auto parent = MakeTransactionRef(CreateValidMempoolTransaction(m_coinbase_txns[i + 1], 0, 0, coinbaseKey, spk, CAmount(48 * COIN), false)); package.emplace_back(parent); - child.vin.push_back(CTxIn(COutPoint(parent->GetHash(), 0))); + child.vin.emplace_back(COutPoint(parent->GetHash(), 0)); } - child.vout.push_back(CTxOut(47 * COIN, spk2)); + child.vout.emplace_back(47 * COIN, spk2); // The child must be in the package. BOOST_CHECK(!IsChildWithParents(package)); @@ -187,6 +188,7 @@ BOOST_FIXTURE_TEST_CASE(noncontextual_package_tests, TestChain100Setup) PackageValidationState state; BOOST_CHECK(CheckPackage(package, state)); BOOST_CHECK(IsChildWithParents(package)); + BOOST_CHECK(IsChildWithParentsTree(package)); package.erase(package.begin()); BOOST_CHECK(IsChildWithParents(package)); @@ -199,26 +201,27 @@ BOOST_FIXTURE_TEST_CASE(noncontextual_package_tests, TestChain100Setup) // 2 Parents and 1 Child where one parent depends on the other. { CMutableTransaction mtx_parent; - mtx_parent.vin.push_back(CTxIn(COutPoint(m_coinbase_txns[0]->GetHash(), 0))); - mtx_parent.vout.push_back(CTxOut(20 * COIN, spk)); - mtx_parent.vout.push_back(CTxOut(20 * COIN, spk2)); + mtx_parent.vin.emplace_back(COutPoint(m_coinbase_txns[0]->GetHash(), 0)); + mtx_parent.vout.emplace_back(20 * COIN, spk); + mtx_parent.vout.emplace_back(20 * COIN, spk2); CTransactionRef tx_parent = MakeTransactionRef(mtx_parent); CMutableTransaction mtx_parent_also_child; - mtx_parent_also_child.vin.push_back(CTxIn(COutPoint(tx_parent->GetHash(), 0))); - mtx_parent_also_child.vout.push_back(CTxOut(20 * COIN, spk)); + mtx_parent_also_child.vin.emplace_back(COutPoint(tx_parent->GetHash(), 0)); + mtx_parent_also_child.vout.emplace_back(20 * COIN, spk); CTransactionRef tx_parent_also_child = MakeTransactionRef(mtx_parent_also_child); CMutableTransaction mtx_child; - mtx_child.vin.push_back(CTxIn(COutPoint(tx_parent->GetHash(), 1))); - mtx_child.vin.push_back(CTxIn(COutPoint(tx_parent_also_child->GetHash(), 0))); - mtx_child.vout.push_back(CTxOut(39 * COIN, spk)); + mtx_child.vin.emplace_back(COutPoint(tx_parent->GetHash(), 1)); + mtx_child.vin.emplace_back(COutPoint(tx_parent_also_child->GetHash(), 0)); + mtx_child.vout.emplace_back(39 * COIN, spk); CTransactionRef tx_child = MakeTransactionRef(mtx_child); PackageValidationState state; BOOST_CHECK(IsChildWithParents({tx_parent, tx_parent_also_child})); BOOST_CHECK(IsChildWithParents({tx_parent, tx_child})); BOOST_CHECK(IsChildWithParents({tx_parent, tx_parent_also_child, tx_child})); + BOOST_CHECK(!IsChildWithParentsTree({tx_parent, tx_parent_also_child, tx_child})); // IsChildWithParents does not detect unsorted parents. BOOST_CHECK(IsChildWithParents({tx_parent_also_child, tx_parent, tx_child})); BOOST_CHECK(CheckPackage({tx_parent, tx_parent_also_child, tx_child}, state)); @@ -298,7 +301,7 @@ BOOST_FIXTURE_TEST_CASE(package_submission_tests, TestChain100Setup) // missing inputs, so the package validation isn't expected to happen. { CScriptWitness bad_witness; - bad_witness.stack.push_back(std::vector<unsigned char>(1)); + bad_witness.stack.emplace_back(1); CMutableTransaction mtx_parent_invalid{mtx_parent}; mtx_parent_invalid.vin[0].scriptWitness = bad_witness; CTransactionRef tx_parent_invalid = MakeTransactionRef(mtx_parent_invalid); @@ -319,7 +322,7 @@ BOOST_FIXTURE_TEST_CASE(package_submission_tests, TestChain100Setup) } // Child with missing parent. - mtx_child.vin.push_back(CTxIn(COutPoint(package_unrelated[0]->GetHash(), 0))); + mtx_child.vin.emplace_back(COutPoint(package_unrelated[0]->GetHash(), 0)); Package package_missing_parent; package_missing_parent.push_back(tx_parent); package_missing_parent.push_back(MakeTransactionRef(mtx_child)); @@ -401,12 +404,12 @@ BOOST_FIXTURE_TEST_CASE(package_witness_swap_tests, TestChain100Setup) // Make two children with the same txid but different witnesses. CScriptWitness witness1; - witness1.stack.push_back(std::vector<unsigned char>(1)); - witness1.stack.push_back(std::vector<unsigned char>(witnessScript.begin(), witnessScript.end())); + witness1.stack.emplace_back(1); + witness1.stack.emplace_back(witnessScript.begin(), witnessScript.end()); CScriptWitness witness2(witness1); - witness2.stack.push_back(std::vector<unsigned char>(2)); - witness2.stack.push_back(std::vector<unsigned char>(witnessScript.begin(), witnessScript.end())); + witness2.stack.emplace_back(2); + witness2.stack.emplace_back(witnessScript.begin(), witnessScript.end()); CKey child_key; child_key.MakeNewKey(true); @@ -526,7 +529,7 @@ BOOST_FIXTURE_TEST_CASE(package_witness_swap_tests, TestChain100Setup) CScript acs_script = CScript() << OP_TRUE; CScript acs_spk = GetScriptForDestination(WitnessV0ScriptHash(acs_script)); CScriptWitness acs_witness; - acs_witness.stack.push_back(std::vector<unsigned char>(acs_script.begin(), acs_script.end())); + acs_witness.stack.emplace_back(acs_script.begin(), acs_script.end()); // parent1 will already be in the mempool auto mtx_parent1 = CreateValidMempoolTransaction(/*input_transaction=*/m_coinbase_txns[1], /*input_vout=*/0, @@ -540,11 +543,11 @@ BOOST_FIXTURE_TEST_CASE(package_witness_swap_tests, TestChain100Setup) CScript grandparent2_script = CScript() << OP_DROP << OP_TRUE; CScript grandparent2_spk = GetScriptForDestination(WitnessV0ScriptHash(grandparent2_script)); CScriptWitness parent2_witness1; - parent2_witness1.stack.push_back(std::vector<unsigned char>(1)); - parent2_witness1.stack.push_back(std::vector<unsigned char>(grandparent2_script.begin(), grandparent2_script.end())); + parent2_witness1.stack.emplace_back(1); + parent2_witness1.stack.emplace_back(grandparent2_script.begin(), grandparent2_script.end()); CScriptWitness parent2_witness2; - parent2_witness2.stack.push_back(std::vector<unsigned char>(2)); - parent2_witness2.stack.push_back(std::vector<unsigned char>(grandparent2_script.begin(), grandparent2_script.end())); + parent2_witness2.stack.emplace_back(2); + parent2_witness2.stack.emplace_back(grandparent2_script.begin(), grandparent2_script.end()); // Create grandparent2 creating an output with multiple spending paths. Submit to mempool. auto mtx_grandparent2 = CreateValidMempoolTransaction(/*input_transaction=*/m_coinbase_txns[2], /*input_vout=*/0, @@ -590,13 +593,13 @@ BOOST_FIXTURE_TEST_CASE(package_witness_swap_tests, TestChain100Setup) CScript mixed_child_spk = GetScriptForDestination(WitnessV0KeyHash(mixed_grandchild_key.GetPubKey())); CMutableTransaction mtx_mixed_child; - mtx_mixed_child.vin.push_back(CTxIn(COutPoint(ptx_parent1->GetHash(), 0))); - mtx_mixed_child.vin.push_back(CTxIn(COutPoint(ptx_parent2_v1->GetHash(), 0))); - mtx_mixed_child.vin.push_back(CTxIn(COutPoint(ptx_parent3->GetHash(), 0))); + mtx_mixed_child.vin.emplace_back(COutPoint(ptx_parent1->GetHash(), 0)); + mtx_mixed_child.vin.emplace_back(COutPoint(ptx_parent2_v1->GetHash(), 0)); + mtx_mixed_child.vin.emplace_back(COutPoint(ptx_parent3->GetHash(), 0)); mtx_mixed_child.vin[0].scriptWitness = acs_witness; mtx_mixed_child.vin[1].scriptWitness = acs_witness; mtx_mixed_child.vin[2].scriptWitness = acs_witness; - mtx_mixed_child.vout.push_back(CTxOut((48 + 49 + 50 - 1) * COIN, mixed_child_spk)); + mtx_mixed_child.vout.emplace_back((48 + 49 + 50 - 1) * COIN, mixed_child_spk); CTransactionRef ptx_mixed_child = MakeTransactionRef(mtx_mixed_child); package_mixed.push_back(ptx_mixed_child); diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp index 2947bc3fcb..e70c105c8a 100644 --- a/src/test/util/setup_common.cpp +++ b/src/test/util/setup_common.cpp @@ -256,6 +256,7 @@ TestingSetup::TestingSetup( m_node.connman = std::make_unique<ConnmanTestMsg>(0x1337, 0x1337, *m_node.addrman, *m_node.netgroupman, Params()); // Deterministic randomness for tests. PeerManager::Options peerman_opts; ApplyArgsManOptions(*m_node.args, peerman_opts); + peerman_opts.deterministic_rng = true; m_node.peerman = PeerManager::make(*m_node.connman, *m_node.addrman, m_node.banman.get(), *m_node.chainman, *m_node.mempool, peerman_opts); @@ -399,7 +400,7 @@ std::vector<CTransactionRef> TestChain100Setup::PopulateMempool(FastRandomContex for (size_t n{0}; n < num_inputs; ++n) { if (unspent_prevouts.empty()) break; const auto& [prevout, amount] = unspent_prevouts.front(); - mtx.vin.push_back(CTxIn(prevout, CScript())); + mtx.vin.emplace_back(prevout, CScript()); total_in += amount; unspent_prevouts.pop_front(); } @@ -408,7 +409,7 @@ std::vector<CTransactionRef> TestChain100Setup::PopulateMempool(FastRandomContex const CAmount amount_per_output = (total_in - fee) / num_outputs; for (size_t n{0}; n < num_outputs; ++n) { CScript spk = CScript() << CScriptNum(num_transactions + n); - mtx.vout.push_back(CTxOut(amount_per_output, spk)); + mtx.vout.emplace_back(amount_per_output, spk); } CTransactionRef ptx = MakeTransactionRef(mtx); mempool_transactions.push_back(ptx); @@ -417,7 +418,7 @@ std::vector<CTransactionRef> TestChain100Setup::PopulateMempool(FastRandomContex // it can be used to build a more complex transaction graph. Insert randomly into // unspent_prevouts for extra randomness in the resulting structures. for (size_t n{0}; n < num_outputs; ++n) { - unspent_prevouts.push_back(std::make_pair(COutPoint(ptx->GetHash(), n), amount_per_output)); + unspent_prevouts.emplace_back(COutPoint(ptx->GetHash(), n), amount_per_output); std::swap(unspent_prevouts.back(), unspent_prevouts[det_rand.randrange(unspent_prevouts.size())]); } } @@ -447,8 +448,8 @@ void TestChain100Setup::MockMempoolMinFee(const CFeeRate& target_feerate) // Manually create an invalid transaction. Manually set the fee in the CTxMemPoolEntry to // achieve the exact target feerate. CMutableTransaction mtx = CMutableTransaction(); - mtx.vin.push_back(CTxIn{COutPoint{g_insecure_rand_ctx.rand256(), 0}}); - mtx.vout.push_back(CTxOut(1 * COIN, GetScriptForDestination(WitnessV0ScriptHash(CScript() << OP_TRUE)))); + mtx.vin.emplace_back(COutPoint{g_insecure_rand_ctx.rand256(), 0}); + mtx.vout.emplace_back(1 * COIN, GetScriptForDestination(WitnessV0ScriptHash(CScript() << OP_TRUE))); const auto tx{MakeTransactionRef(mtx)}; LockPoints lp; // The new mempool min feerate is equal to the removed package's feerate + incremental feerate. diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp index 67f71bd266..7d6c96ab40 100644 --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -1023,9 +1023,9 @@ BOOST_AUTO_TEST_CASE(test_FormatParagraph) BOOST_AUTO_TEST_CASE(test_FormatSubVersion) { std::vector<std::string> comments; - comments.push_back(std::string("comment1")); + comments.emplace_back("comment1"); std::vector<std::string> comments2; - comments2.push_back(std::string("comment1")); + comments2.emplace_back("comment1"); comments2.push_back(SanitizeString(std::string("Comment2; .,_?@-; !\"#$%&'()*+/<=>[]\\^`{|}~"), SAFE_CHARS_UA_COMMENT)); // Semicolon is discouraged but not forbidden by BIP-0014 BOOST_CHECK_EQUAL(FormatSubVersion("Test", 99900, std::vector<std::string>()),std::string("/Test:9.99.0/")); BOOST_CHECK_EQUAL(FormatSubVersion("Test", 99900, comments),std::string("/Test:9.99.0(comment1)/")); diff --git a/src/test/util_threadnames_tests.cpp b/src/test/util_threadnames_tests.cpp index ae913939e8..45d3a58fd3 100644 --- a/src/test/util_threadnames_tests.cpp +++ b/src/test/util_threadnames_tests.cpp @@ -40,7 +40,7 @@ std::set<std::string> RenameEnMasse(int num_threads) threads.reserve(num_threads); for (int i = 0; i < num_threads; ++i) { - threads.push_back(std::thread(RenameThisThread, i)); + threads.emplace_back(RenameThisThread, i); } for (std::thread& thread : threads) thread.join(); diff --git a/src/test/validation_block_tests.cpp b/src/test/validation_block_tests.cpp index 411371f7c1..64cb5522eb 100644 --- a/src/test/validation_block_tests.cpp +++ b/src/test/validation_block_tests.cpp @@ -117,7 +117,7 @@ std::shared_ptr<const CBlock> MinerTestingSetup::BadBlock(const uint256& prev_ha auto pblock = Block(prev_hash); CMutableTransaction coinbase_spend; - coinbase_spend.vin.push_back(CTxIn(COutPoint(pblock->vtx[0]->GetHash(), 0), CScript(), 0)); + coinbase_spend.vin.emplace_back(COutPoint(pblock->vtx[0]->GetHash(), 0), CScript(), 0); coinbase_spend.vout.push_back(pblock->vtx[0]->vout[0]); CTransactionRef tx = MakeTransactionRef(coinbase_spend); @@ -245,7 +245,7 @@ BOOST_AUTO_TEST_CASE(mempool_locks_reorg) std::vector<CTransactionRef> txs; for (int num_txs = 22; num_txs > 0; --num_txs) { CMutableTransaction mtx; - mtx.vin.push_back(CTxIn{COutPoint{last_mined->vtx[0]->GetHash(), 1}, CScript{}}); + mtx.vin.emplace_back(COutPoint{last_mined->vtx[0]->GetHash(), 1}, CScript{}); mtx.vin[0].scriptWitness.stack.push_back(WITNESS_STACK_ELEM_OP_TRUE); mtx.vout.push_back(last_mined->vtx[0]->vout[1]); mtx.vout[0].nValue -= 1000; diff --git a/src/test/validation_tests.cpp b/src/test/validation_tests.cpp index d34d98c219..2692037273 100644 --- a/src/test/validation_tests.cpp +++ b/src/test/validation_tests.cpp @@ -138,11 +138,11 @@ BOOST_AUTO_TEST_CASE(test_assumeutxo) const auto out110 = *params->AssumeutxoForHeight(110); BOOST_CHECK_EQUAL(out110.hash_serialized.ToString(), "1ebbf5850204c0bdb15bf030f47c7fe91d45c44c712697e4509ba67adb01c618"); - BOOST_CHECK_EQUAL(out110.nChainTx, 110U); + BOOST_CHECK_EQUAL(out110.nChainTx, 111U); const auto out110_2 = *params->AssumeutxoForBlockhash(uint256S("0x696e92821f65549c7ee134edceeeeaaa4105647a3c4fd9f298c0aec0ab50425c")); BOOST_CHECK_EQUAL(out110_2.hash_serialized.ToString(), "1ebbf5850204c0bdb15bf030f47c7fe91d45c44c712697e4509ba67adb01c618"); - BOOST_CHECK_EQUAL(out110_2.nChainTx, 110U); + BOOST_CHECK_EQUAL(out110_2.nChainTx, 111U); } BOOST_AUTO_TEST_SUITE_END() |