|
5 | 5 | #include <test/data/script_tests.json.h>
|
6 | 6 |
|
7 | 7 | #include <core_io.h>
|
| 8 | +#include <fs.h> |
8 | 9 | #include <key.h>
|
9 | 10 | #include <rpc/util.h>
|
10 | 11 | #include <script/script.h>
|
11 | 12 | #include <script/script_error.h>
|
| 13 | +#include <script/sigcache.h> |
12 | 14 | #include <script/sign.h>
|
13 | 15 | #include <script/signingprovider.h>
|
14 | 16 | #include <streams.h>
|
@@ -1339,13 +1341,41 @@ BOOST_AUTO_TEST_CASE(script_GetScriptAsm)
|
1339 | 1341 | BOOST_CHECK_EQUAL(derSig + "83 " + pubKey, ScriptToAsmStr(CScript() << ToByteVector(ParseHex(derSig + "83")) << vchPubKey));
|
1340 | 1342 | }
|
1341 | 1343 |
|
1342 |
| -static CScript |
1343 |
| -ScriptFromHex(const char* hex) |
| 1344 | +static CScript ScriptFromHex(const std::string& str) |
1344 | 1345 | {
|
1345 |
| - std::vector<unsigned char> data = ParseHex(hex); |
| 1346 | + std::vector<unsigned char> data = ParseHex(str); |
1346 | 1347 | return CScript(data.begin(), data.end());
|
1347 | 1348 | }
|
1348 | 1349 |
|
| 1350 | +static CMutableTransaction TxFromHex(const std::string& str) |
| 1351 | +{ |
| 1352 | + CMutableTransaction tx; |
| 1353 | + VectorReader(SER_DISK, SERIALIZE_TRANSACTION_NO_WITNESS, ParseHex(str), 0) >> tx; |
| 1354 | + return tx; |
| 1355 | +} |
| 1356 | + |
| 1357 | +static std::vector<CTxOut> TxOutsFromJSON(const UniValue& univalue) |
| 1358 | +{ |
| 1359 | + assert(univalue.isArray()); |
| 1360 | + std::vector<CTxOut> prevouts; |
| 1361 | + for (size_t i = 0; i < univalue.size(); ++i) { |
| 1362 | + CTxOut txout; |
| 1363 | + VectorReader(SER_DISK, 0, ParseHex(univalue[i].get_str()), 0) >> txout; |
| 1364 | + prevouts.push_back(std::move(txout)); |
| 1365 | + } |
| 1366 | + return prevouts; |
| 1367 | +} |
| 1368 | + |
| 1369 | +static CScriptWitness ScriptWitnessFromJSON(const UniValue& univalue) |
| 1370 | +{ |
| 1371 | + assert(univalue.isArray()); |
| 1372 | + CScriptWitness scriptwitness; |
| 1373 | + for (size_t i = 0; i < univalue.size(); ++i) { |
| 1374 | + auto bytes = ParseHex(univalue[i].get_str()); |
| 1375 | + scriptwitness.stack.push_back(std::move(bytes)); |
| 1376 | + } |
| 1377 | + return scriptwitness; |
| 1378 | +} |
1349 | 1379 |
|
1350 | 1380 | BOOST_AUTO_TEST_CASE(script_FindAndDelete)
|
1351 | 1381 | {
|
@@ -1610,5 +1640,104 @@ BOOST_AUTO_TEST_CASE(bitcoinconsensus_verify_script_invalid_flags)
|
1610 | 1640 | BOOST_CHECK_EQUAL(err, bitcoinconsensus_ERR_INVALID_FLAGS);
|
1611 | 1641 | }
|
1612 | 1642 |
|
| 1643 | +static std::vector<unsigned int> AllConsensusFlags() |
| 1644 | +{ |
| 1645 | + std::vector<unsigned int> ret; |
| 1646 | + |
| 1647 | + for (unsigned int i = 0; i < 128; ++i) { |
| 1648 | + unsigned int flag = 0; |
| 1649 | + if (i & 1) flag |= SCRIPT_VERIFY_P2SH; |
| 1650 | + if (i & 2) flag |= SCRIPT_VERIFY_DERSIG; |
| 1651 | + if (i & 4) flag |= SCRIPT_VERIFY_NULLDUMMY; |
| 1652 | + if (i & 8) flag |= SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY; |
| 1653 | + if (i & 16) flag |= SCRIPT_VERIFY_CHECKSEQUENCEVERIFY; |
| 1654 | + if (i & 32) flag |= SCRIPT_VERIFY_WITNESS; |
| 1655 | + if (i & 64) flag |= SCRIPT_VERIFY_TAPROOT; |
| 1656 | + |
| 1657 | + // SCRIPT_VERIFY_WITNESS requires SCRIPT_VERIFY_P2SH |
| 1658 | + if (flag & SCRIPT_VERIFY_WITNESS && !(flag & SCRIPT_VERIFY_P2SH)) continue; |
| 1659 | + // SCRIPT_VERIFY_TAPROOT requires SCRIPT_VERIFY_WITNESS |
| 1660 | + if (flag & SCRIPT_VERIFY_TAPROOT && !(flag & SCRIPT_VERIFY_WITNESS)) continue; |
| 1661 | + |
| 1662 | + ret.push_back(flag); |
| 1663 | + } |
| 1664 | + |
| 1665 | + return ret; |
| 1666 | +} |
| 1667 | + |
| 1668 | +/** Precomputed list of all valid combinations of consensus-relevant script validation flags. */ |
| 1669 | +static const std::vector<unsigned int> ALL_CONSENSUS_FLAGS = AllConsensusFlags(); |
| 1670 | + |
| 1671 | +static void AssetTest(const UniValue& test) |
| 1672 | +{ |
| 1673 | + BOOST_CHECK(test.isObject()); |
| 1674 | + |
| 1675 | + CMutableTransaction mtx = TxFromHex(test["tx"].get_str()); |
| 1676 | + const std::vector<CTxOut> prevouts = TxOutsFromJSON(test["prevouts"]); |
| 1677 | + BOOST_CHECK(prevouts.size() == mtx.vin.size()); |
| 1678 | + size_t idx = test["index"].get_int64(); |
| 1679 | + unsigned int test_flags = ParseScriptFlags(test["flags"].get_str()); |
| 1680 | + bool fin = test.exists("final") && test["final"].get_bool(); |
| 1681 | + |
| 1682 | + if (test.exists("success")) { |
| 1683 | + mtx.vin[idx].scriptSig = ScriptFromHex(test["success"]["scriptSig"].get_str()); |
| 1684 | + mtx.vin[idx].scriptWitness = ScriptWitnessFromJSON(test["success"]["witness"]); |
| 1685 | + CTransaction tx(mtx); |
| 1686 | + PrecomputedTransactionData txdata; |
| 1687 | + txdata.Init(tx, std::vector<CTxOut>(prevouts)); |
| 1688 | + CachingTransactionSignatureChecker txcheck(&tx, idx, prevouts[idx].nValue, true, txdata); |
| 1689 | + for (const auto flags : ALL_CONSENSUS_FLAGS) { |
| 1690 | + // "final": true tests are valid for all flags. Others are only valid with flags that are |
| 1691 | + // a subset of test_flags. |
| 1692 | + if (fin || ((flags & test_flags) == flags)) { |
| 1693 | + bool ret = VerifyScript(tx.vin[idx].scriptSig, prevouts[idx].scriptPubKey, &tx.vin[idx].scriptWitness, flags, txcheck, nullptr); |
| 1694 | + BOOST_CHECK(ret); |
| 1695 | + } |
| 1696 | + } |
| 1697 | + } |
| 1698 | + |
| 1699 | + if (test.exists("failure")) { |
| 1700 | + mtx.vin[idx].scriptSig = ScriptFromHex(test["failure"]["scriptSig"].get_str()); |
| 1701 | + mtx.vin[idx].scriptWitness = ScriptWitnessFromJSON(test["failure"]["witness"]); |
| 1702 | + CTransaction tx(mtx); |
| 1703 | + PrecomputedTransactionData txdata; |
| 1704 | + txdata.Init(tx, std::vector<CTxOut>(prevouts)); |
| 1705 | + CachingTransactionSignatureChecker txcheck(&tx, idx, prevouts[idx].nValue, true, txdata); |
| 1706 | + for (const auto flags : ALL_CONSENSUS_FLAGS) { |
| 1707 | + // If a test is supposed to fail with test_flags, it should also fail with any superset thereof. |
| 1708 | + if ((flags & test_flags) == test_flags) { |
| 1709 | + bool ret = VerifyScript(tx.vin[idx].scriptSig, prevouts[idx].scriptPubKey, &tx.vin[idx].scriptWitness, flags, txcheck, nullptr); |
| 1710 | + BOOST_CHECK(!ret); |
| 1711 | + } |
| 1712 | + } |
| 1713 | + } |
| 1714 | +} |
| 1715 | + |
| 1716 | +BOOST_AUTO_TEST_CASE(script_assets_test) |
| 1717 | +{ |
| 1718 | + const char* dir = std::getenv("DIR_UNIT_TEST_DATA"); |
| 1719 | + BOOST_WARN_MESSAGE(dir != nullptr, "Variable DIR_UNIT_TEST_DATA unset, skipping script_assets_test"); |
| 1720 | + if (dir == nullptr) return; |
| 1721 | + auto path = fs::path(dir) / "script_assets_test.json"; |
| 1722 | + bool exists = fs::exists(path); |
| 1723 | + BOOST_WARN_MESSAGE(exists, "File $DIR_UNIT_TEST_DATA/script_assets_test.json not found, skipping script_assets_test"); |
| 1724 | + if (!exists) return; |
| 1725 | + fs::ifstream file(path); |
| 1726 | + BOOST_CHECK(file.is_open()); |
| 1727 | + file.seekg(0, std::ios::end); |
| 1728 | + size_t length = file.tellg(); |
| 1729 | + file.seekg(0, std::ios::beg); |
| 1730 | + std::string data(length, '\0'); |
| 1731 | + file.read(&data[0], data.size()); |
| 1732 | + UniValue tests = read_json(data); |
| 1733 | + BOOST_CHECK(tests.isArray()); |
| 1734 | + BOOST_CHECK(tests.size() > 0); |
| 1735 | + |
| 1736 | + for (size_t i = 0; i < tests.size(); i++) { |
| 1737 | + AssetTest(tests[i]); |
| 1738 | + } |
| 1739 | + file.close(); |
| 1740 | +} |
| 1741 | + |
1613 | 1742 | #endif
|
1614 | 1743 | BOOST_AUTO_TEST_SUITE_END()
|
0 commit comments