Repository: bitcoin/bitcoin
File: src/script/interpreter.cpp (C++)

Code before:
    std::vector<unsigned char> witnessprogram;
    if (scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram)) {
        return WitnessSigOps(witnessversion, witnessprogram, witness ? *witness : witnessEmpty);
    }

    if (scriptPubKey.IsPayToScriptHash() && scriptSig.IsPushOnly()) {
        CScript::const_iterator pc = scriptSig.begin();
        std::vector<unsigned char> data;
        while (pc < scriptSig.end()) {
            opcodetype opcode;
            scriptSig.GetOp(pc, opcode, data);
        }
        CScript subscript(data.begin(), data.end());
        if (subscript.IsWitnessProgram(witnessversion, witnessprogram)) {
            return WitnessSigOps(witnessversion, witnessprogram, witness ? *witness : witnessEmpty);
        }
    }

    return 0;
}

#define FLAG_NAME(flag) {std::string(#flag), SCRIPT_VERIFY_##flag}
const std::map<std::string, script_verify_flag_name> g_verify_flag_names{
    FLAG_NAME(P2SH),
    FLAG_NAME(STRICTENC),
    FLAG_NAME(DERSIG),
    FLAG_NAME(LOW_S),
    FLAG_NAME(SIGPUSHONLY),
    FLAG_NAME(MINIMALDATA),
    FLAG_NAME(NULLDUMMY),
    FLAG_NAME(DISCOURAGE_UPGRADABLE_NOPS),
    FLAG_NAME(CLEANSTACK),
    FLAG_NAME(MINIMALIF),
    FLAG_NAME(NULLFAIL),
    FLAG_NAME(CHECKLOCKTIMEVERIFY),
    FLAG_NAME(CHECKSEQUENCEVERIFY),
    FLAG_NAME(WITNESS),
    FLAG_NAME(DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM),
    FLAG_NAME(WITNESS_PUBKEYTYPE),
    FLAG_NAME(CONST_SCRIPTCODE),
    FLAG_NAME(TAPROOT),
    FLAG_NAME(DISCOURAGE_UPGRADABLE_PUBKEYTYPE),
    FLAG_NAME(DISCOURAGE_OP_SUCCESS),
    FLAG_NAME(DISCOURAGE_UPGRADABLE_TAPROOT_VERSION),
};
#undef FLAG_NAME

std::vector<std::string> GetScriptFlagNames(script_verify_flags flags)
{
    std::vector<std::string> res;
    if (flags == SCRIPT_VERIFY_NONE) {

Review comment (bug):
An alternative approach would be to use a `constexpr` array and make the whole thing compile-time? As a nice benefit, removes the `FLAG_NAME` macro which imo doesn't simplify things enough to be worth it.

<details>
<summary>git diff on faa9d10c84</summary>

```diff
diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp
index abd99fc365..731c0a070f 100644
--- a/src/script/interpreter.cpp
+++ b/src/script/interpreter.cpp
@@ -2163,36 +2163,6 @@ size_t CountWitnessSigOps(const CScript& scriptSig, const CScript& scriptPubKey,
     return 0;
 }
 
-const std::map<std::string, script_verify_flag_name>& ScriptFlagNamesToEnum()
-{
-#define FLAG_NAME(flag) {std::string(#flag), SCRIPT_VERIFY_##flag}
-    static const std::map<std::string, script_verify_flag_name> g_names_to_enum{
-        FLAG_NAME(P2SH),
-        FLAG_NAME(STRICTENC),
-        FLAG_NAME(DERSIG),
-        FLAG_NAME(LOW_S),
-        FLAG_NAME(SIGPUSHONLY),
-        FLAG_NAME(MINIMALDATA),
-        FLAG_NAME(NULLDUMMY),
-        FLAG_NAME(DISCOURAGE_UPGRADABLE_NOPS),
-        FLAG_NAME(CLEANSTACK),
-        FLAG_NAME(MINIMALIF),
-        FLAG_NAME(NULLFAIL),
-        FLAG_NAME(CHECKLOCKTIMEVERIFY),
-        FLAG_NAME(CHECKSEQUENCEVERIFY),
-        FLAG_NAME(WITNESS),
-        FLAG_NAME(DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM),
-        FLAG_NAME(WITNESS_PUBKEYTYPE),
-        FLAG_NAME(CONST_SCRIPTCODE),
-        FLAG_NAME(TAPROOT),
-        FLAG_NAME(DISCOURAGE_UPGRADABLE_PUBKEYTYPE),
-        FLAG_NAME(DISCOURAGE_OP_SUCCESS),
-        FLAG_NAME(DISCOURAGE_UPGRADABLE_TAPROOT_VERSION),
-    };
-#undef FLAG_NAME
-    return g_names_to_enum;
-}
-
 std::vector<std::string> GetScriptFlagNames(script_verify_flags flags)
 {
     std::vector<std::string> res;
@@ -2200,9 +2170,9 @@ std::vector<std::string> GetScriptFlagNames(script_verify_flags flags)
         return res;
     }
     script_verify_flags leftover = flags;
-    for (const auto& [name, flag] : ScriptFlagNamesToEnum()) {
+    for (const auto& [name, flag] : SCRIPT_FLAG_NAMES) {
         if ((flags & flag) != 0) {
-            res.push_back(name);
+            res.emplace_back(name);
             leftover &= ~flag;
         }
     }
diff --git a/src/script/interpreter.h b/src/script/interpreter.h
index 7f8a1f7a41..04611bc5d6 100644
--- a/src/script/interpreter.h
+++ b/src/script/interpreter.h
@@ -381,7 +381,29 @@ size_t CountWitnessSigOps(const CScript& scriptSig, const CScript& scriptPubKey,
 
 int FindAndDelete(CScript& script, const CScript& b);
 
-const std::map<std::string, script_verify_flag_name>& ScriptFlagNamesToEnum();
+inline constexpr std::array SCRIPT_FLAG_NAMES{std::to_array<std::pair<std::string_view, script_verify_flag_name>>({
+    {"CHECKLOCKTIMEVERIFY", SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY},
+    {"CHECKSEQUENCEVERIFY", SCRIPT_VERIFY_CHECKSEQUENCEVERIFY},
+    {"CLEANSTACK", SCRIPT_VERIFY_CLEANSTACK},
+    {"CONST_SCRIPTCODE", SCRIPT_VERIFY_CONST_SCRIPTCODE},
+    {"DERSIG", SCRIPT_VERIFY_DERSIG},
+    {"DISCOURAGE_OP_SUCCESS", SCRIPT_VERIFY_DISCOURAGE_OP_SUCCESS},
+    {"DISCOURAGE_UPGRADABLE_NOPS", SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS},
+    {"DISCOURAGE_UPGRADABLE_PUBKEYTYPE", SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_PUBKEYTYPE},
+    {"DISCOURAGE_UPGRADABLE_TAPROOT_VERSION", SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION},
+    {"DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM", SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM},
+    {"LOW_S", SCRIPT_VERIFY_LOW_S},
+    {"MINIMALDATA", SCRIPT_VERIFY_MINIMALDATA},
+    {"MINIMALIF", SCRIPT_VERIFY_MINIMALIF},
+    {"NULLDUMMY", SCRIPT_VERIFY_NULLDUMMY},
+    {"NULLFAIL", SCRIPT_VERIFY_NULLFAIL},
+    {"P2SH", SCRIPT_VERIFY_P2SH},
+    {"SIGPUSHONLY", SCRIPT_VERIFY_SIGPUSHONLY},
+    {"STRICTENC", SCRIPT_VERIFY_STRICTENC},
+    {"TAPROOT", SCRIPT_VERIFY_TAPROOT},
+    {"WITNESS", SCRIPT_VERIFY_WITNESS},
+    {"WITNESS_PUBKEYTYPE", SCRIPT_VERIFY_WITNESS_PUBKEYTYPE},
+})};
 
 std::vector<std::string> GetScriptFlagNames(script_verify_flags flags);
 
diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp
index 0ab9fbb479..e234153825 100644
--- a/src/test/transaction_tests.cpp
+++ b/src/test/transaction_tests.cpp
@@ -50,7 +50,6 @@ typedef std::vector<unsigned char> valtype;
 static CFeeRate g_dust{DUST_RELAY_TX_FEE};
 static bool g_bare_multi{DEFAULT_PERMIT_BAREMULTISIG};
 
-static const std::map<std::string, script_verify_flag_name>& mapFlagNames = ScriptFlagNamesToEnum();
 
 script_verify_flags ParseScriptFlags(std::string strFlags)
 {
@@ -60,20 +59,21 @@ script_verify_flags ParseScriptFlags(std::string strFlags)
     std::vector<std::string> words = SplitString(strFlags, ',');
     for (const std::string& word : words)
     {
-        if (!mapFlagNames.count(word)) {
+        auto it = std::ranges::find(SCRIPT_FLAG_NAMES, word, &decltype(SCRIPT_FLAG_NAMES)::value_type::first);
+        if (it == SCRIPT_FLAG_NAMES.end()) {
             BOOST_ERROR("Bad test: unknown verification flag '" << word << "'");
             continue;
         }
-        flags |= mapFlagNames.at(word);
+        flags |= it->second;
     }
     return flags;
 }
 
-// Check that all flags in STANDARD_SCRIPT_VERIFY_FLAGS are present in mapFlagNames.
-bool CheckMapFlagNames()
+// Check that all flags in STANDARD_SCRIPT_VERIFY_FLAGS are present in SCRIPT_FLAG_NAMES.
+bool CheckScriptFlagNames()
 {
     script_verify_flags standard_flags_missing{STANDARD_SCRIPT_VERIFY_FLAGS};
-    for (const auto& pair : mapFlagNames) {
+    for (const auto& pair : SCRIPT_FLAG_NAMES) {
         standard_flags_missing &= ~(pair.second);
     }
     return standard_flags_missing == 0;
@@ -142,11 +142,11 @@ script_verify_flags FillFlags(script_verify_flags flags)
 // Exclude each possible script verify flag from flags. Returns a set of these flag combinations
 // that are valid and without duplicates. For example: if flags=1111 and the 4 possible flags are
 // 0001, 0010, 0100, and 1000, this should return the set {0111, 1011, 1101, 1110}.
-// Assumes that mapFlagNames contains all script verify flags.
+// Assumes that SCRIPT_FLAG_NAMES contains all script verify flags.
 std::set<script_verify_flags> ExcludeIndividualFlags(script_verify_flags flags)
 {
     std::set<script_verify_flags> flags_combos;
-    for (const auto& pair : mapFlagNames) {
+    for (const auto& pair : SCRIPT_FLAG_NAMES) {
         script_verify_flags flags_excluding_one = TrimFlags(flags & ~(pair.second));
         if (flags != flags_excluding_one) {
             flags_combos.insert(flags_excluding_one);
@@ -159,7 +159,7 @@ BOOST_FIXTURE_TEST_SUITE(transaction_tests, BasicTestingSetup)
 
 BOOST_AUTO_TEST_CASE(tx_valid)
 {
-    BOOST_CHECK_MESSAGE(CheckMapFlagNames(), "mapFlagNames is missing a script verification flag");
+    BOOST_CHECK_MESSAGE(CheckScriptFlagNames(), "SCRIPT_FLAG_NAMES is missing a script verification flag");
     // Read tests from test/data/tx_valid.json
     UniValue tests = read_json(json_tests::tx_valid);
 
@@ -223,7 +223,7 @@ BOOST_AUTO_TEST_CASE(tx_valid)
                                 "Tx unexpectedly failed: " << strTest);
 
             // Backwards compatibility of script verification flags: Removing any flag(s) should not invalidate a valid transaction
-            for (const auto& [name, flag] : mapFlagNames) {
+            for (const auto& [name, flag] : SCRIPT_FLAG_NAMES) {
                 // Removing individual flags
                 script_verify_flags flags = TrimFlags(~(verify_flags | flag));
                 if (!CheckTxScripts(tx, mapprevOutScriptPubKeys, mapprevOutValues, flags, txdata, strTest, /*expect_valid=*/true)) {
@@ -314,7 +314,7 @@ BOOST_AUTO_TEST_CASE(tx_invalid)
                                 "Tx unexpectedly passed: " << strTest);
 
             // Backwards compatibility of script verification flags: Adding any flag(s) should not validate an invalid transaction
-            for (const auto& [name, flag] : mapFlagNames) {
+            for (const auto& [name, flag] : SCRIPT_FLAG_NAMES) {
                 script_verify_flags flags = FillFlags(verify_flags | flag);
                 // Adding individual flags
                 if (!CheckTxScripts(tx, mapprevOutScriptPubKeys, mapprevOutValues, flags, txdata, strTest, /*expect_valid=*/false)) {

```
</details>

Code after:
    std::vector<unsigned char> witnessprogram;
    if (scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram)) {
        return WitnessSigOps(witnessversion, witnessprogram, witness ? *witness : witnessEmpty);
    }

    if (scriptPubKey.IsPayToScriptHash() && scriptSig.IsPushOnly()) {
        CScript::const_iterator pc = scriptSig.begin();
        std::vector<unsigned char> data;
        while (pc < scriptSig.end()) {
            opcodetype opcode;
            scriptSig.GetOp(pc, opcode, data);
        }
        CScript subscript(data.begin(), data.end());
        if (subscript.IsWitnessProgram(witnessversion, witnessprogram)) {
            return WitnessSigOps(witnessversion, witnessprogram, witness ? *witness : witnessEmpty);
        }
    }

    return 0;
}

const std::map<std::string, script_verify_flag_name>& ScriptFlagNamesToEnum()
{
#define FLAG_NAME(flag) {std::string(#flag), SCRIPT_VERIFY_##flag}
    static const std::map<std::string, script_verify_flag_name> g_names_to_enum{
        FLAG_NAME(P2SH),
        FLAG_NAME(STRICTENC),
        FLAG_NAME(DERSIG),
        FLAG_NAME(LOW_S),
        FLAG_NAME(SIGPUSHONLY),
        FLAG_NAME(MINIMALDATA),
        FLAG_NAME(NULLDUMMY),
        FLAG_NAME(DISCOURAGE_UPGRADABLE_NOPS),
        FLAG_NAME(CLEANSTACK),
        FLAG_NAME(MINIMALIF),
        FLAG_NAME(NULLFAIL),
        FLAG_NAME(CHECKLOCKTIMEVERIFY),
        FLAG_NAME(CHECKSEQUENCEVERIFY),
        FLAG_NAME(WITNESS),
        FLAG_NAME(DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM),
        FLAG_NAME(WITNESS_PUBKEYTYPE),
        FLAG_NAME(CONST_SCRIPTCODE),
        FLAG_NAME(TAPROOT),
        FLAG_NAME(DISCOURAGE_UPGRADABLE_PUBKEYTYPE),
        FLAG_NAME(DISCOURAGE_OP_SUCCESS),
        FLAG_NAME(DISCOURAGE_UPGRADABLE_TAPROOT_VERSION),
    };
#undef FLAG_NAME
    return g_names_to_enum;
}