script/verify_flags: make script_verify_flags type safe

`using script_verify_flags = uint32_t` allows implicit conversion to
and from int, so replace it with a class to have the compiler ensure we
use the correct type. Provide from_int and as_int to allow for explicit
conversions when desired.

Introduces the type `script_verify_flag_name` for the individual flag
name enumeration.
This commit is contained in:
Anthony Towns
2025-07-26 18:51:24 +10:00
parent a5ead122fe
commit bddcadee82
12 changed files with 100 additions and 25 deletions

View File

@@ -2164,7 +2164,7 @@ size_t CountWitnessSigOps(const CScript& scriptSig, const CScript& scriptPubKey,
}
#define FLAG_NAME(flag) {std::string(#flag), SCRIPT_VERIFY_##flag}
const std::map<std::string, uint32_t> g_verify_flag_names{
const std::map<std::string, script_verify_flag_name> g_verify_flag_names{
FLAG_NAME(P2SH),
FLAG_NAME(STRICTENC),
FLAG_NAME(DERSIG),
@@ -2203,7 +2203,7 @@ std::vector<std::string> GetScriptFlagNames(script_verify_flags flags)
}
}
if (leftover != 0) {
res.push_back(strprintf("0x%08x", leftover));
res.push_back(strprintf("0x%08x", leftover.as_int()));
}
return res;
}

View File

@@ -14,6 +14,7 @@
#include <span.h>
#include <uint256.h>
#include <bit>
#include <cstddef>
#include <cstdint>
#include <optional>
@@ -43,9 +44,10 @@ enum
* All flags are intended to be soft forks: the set of acceptable scripts under
* flags (A | B) is a subset of the acceptable scripts under flag (A).
*/
enum : uint32_t {
SCRIPT_VERIFY_NONE = 0,
static constexpr script_verify_flags SCRIPT_VERIFY_NONE{0};
enum class script_verify_flag_name : uint32_t {
// Evaluate P2SH subscripts (BIP16).
SCRIPT_VERIFY_P2SH = (1U << 0),
@@ -148,6 +150,13 @@ enum : uint32_t {
//
SCRIPT_VERIFY_END_MARKER
};
using enum script_verify_flag_name;
// assert there is still a spare bit
static_assert(static_cast<script_verify_flags::value_type>(SCRIPT_VERIFY_END_MARKER) < (1u << 31));
static constexpr script_verify_flags::value_type MAX_SCRIPT_VERIFY_FLAGS = ((static_cast<script_verify_flags::value_type>(SCRIPT_VERIFY_END_MARKER) - 1) << 1) - 1;
static constexpr int MAX_SCRIPT_VERIFY_FLAGS_BITS = std::bit_width(MAX_SCRIPT_VERIFY_FLAGS);
bool CheckSignatureEncoding(const std::vector<unsigned char> &vchSig, script_verify_flags flags, ScriptError* serror);
@@ -372,7 +381,7 @@ size_t CountWitnessSigOps(const CScript& scriptSig, const CScript& scriptPubKey,
int FindAndDelete(CScript& script, const CScript& b);
extern const std::map<std::string, uint32_t> g_verify_flag_names;
extern const std::map<std::string, script_verify_flag_name> g_verify_flag_names;
std::vector<std::string> GetScriptFlagNames(script_verify_flags flags);

View File

@@ -6,8 +6,66 @@
#ifndef BITCOIN_SCRIPT_VERIFY_FLAGS_H
#define BITCOIN_SCRIPT_VERIFY_FLAGS_H
#include <compare>
#include <cstdint>
using script_verify_flags = uint32_t;
enum class script_verify_flag_name : uint32_t;
class script_verify_flags
{
public:
using value_type = uint32_t;
consteval script_verify_flags() = default;
// also allow construction with hard-coded 0 (but not other integers)
consteval explicit(false) script_verify_flags(value_type f) : m_value{f} { if (f != 0) throw 0; }
// implicit construction from a hard-coded SCRIPT_VERIFY_* constant is also okay
constexpr explicit(false) script_verify_flags(script_verify_flag_name f) : m_value{static_cast<value_type>(f)} { }
// rule of 5
constexpr script_verify_flags(const script_verify_flags&) = default;
constexpr script_verify_flags(script_verify_flags&&) = default;
constexpr script_verify_flags& operator=(const script_verify_flags&) = default;
constexpr script_verify_flags& operator=(script_verify_flags&&) = default;
constexpr ~script_verify_flags() = default;
// integer conversion needs to be very explicit
static constexpr script_verify_flags from_int(value_type f) { script_verify_flags r; r.m_value = f; return r; }
constexpr value_type as_int() const { return m_value; }
// bitwise operations
constexpr script_verify_flags operator~() const { return from_int(~m_value); }
friend constexpr script_verify_flags operator|(script_verify_flags a, script_verify_flags b) { return from_int(a.m_value | b.m_value); }
friend constexpr script_verify_flags operator&(script_verify_flags a, script_verify_flags b) { return from_int(a.m_value & b.m_value); }
// in-place bitwise operations
constexpr script_verify_flags& operator|=(script_verify_flags vf) { m_value |= vf.m_value; return *this; }
constexpr script_verify_flags& operator&=(script_verify_flags vf) { m_value &= vf.m_value; return *this; }
// tests
constexpr explicit operator bool() const { return m_value != 0; }
constexpr bool operator==(script_verify_flags other) const { return m_value == other.m_value; }
/** Compare two script_verify_flags. <, >, <=, and >= are auto-generated from this. */
friend constexpr std::strong_ordering operator<=>(const script_verify_flags& a, const script_verify_flags& b) noexcept
{
return a.m_value <=> b.m_value;
}
private:
value_type m_value{0}; // default value is SCRIPT_VERIFY_NONE
};
inline constexpr script_verify_flags operator~(script_verify_flag_name f)
{
return ~script_verify_flags{f};
}
inline constexpr script_verify_flags operator|(script_verify_flag_name f1, script_verify_flag_name f2)
{
return script_verify_flags{f1} | f2;
}
#endif // BITCOIN_SCRIPT_VERIFY_FLAGS_H