diff --git a/src/addrdb.cpp b/src/addrdb.cpp index cac7c380f9f1..a1a536ea2b4f 100644 --- a/src/addrdb.cpp +++ b/src/addrdb.cpp @@ -226,7 +226,8 @@ util::Result> LoadAddrman(const NetGroupManager& netgro return util::Error{strprintf(_("Invalid or corrupt peers.dat (%s). If you believe this is a bug, please report it to %s. As a workaround, you can move the file (%s) out of the way (rename, move, or delete) to have a new one created on the next start."), e.what(), PACKAGE_BUGREPORT, fs::quoted(fs::PathToString(path_addr)))}; } - return addrman; // std::move should be unneccessary but is temporarily needed to work around clang bug (https://github.com/bitcoin/bitcoin/pull/25977#issuecomment-1561270092) + return {std::move(addrman)}; // std::move should be unnecessary but is temporarily needed to work around clang bug + // (https://github.com/bitcoin/bitcoin/pull/25977#issuecomment-1561270092) } void DumpAnchors(const fs::path& anchors_db_path, const std::vector& anchors) diff --git a/src/addrdb.h b/src/addrdb.h index 164191633abb..cc3014dce29b 100644 --- a/src/addrdb.h +++ b/src/addrdb.h @@ -6,13 +6,11 @@ #ifndef BITCOIN_ADDRDB_H #define BITCOIN_ADDRDB_H -#include // For banmap_t -#include +#include #include #include #include -#include #include class ArgsManager; @@ -20,7 +18,6 @@ class AddrMan; class CAddress; class DataStream; class NetGroupManager; -struct bilingual_str; /** Only used by tests. */ void ReadFromStream(AddrMan& addr, DataStream& ssPeers); diff --git a/src/kernel/checks.h b/src/kernel/checks.h index 3eb14824fb6f..82807329f24f 100644 --- a/src/kernel/checks.h +++ b/src/kernel/checks.h @@ -16,8 +16,8 @@ struct Context; /** * Ensure a usable environment with all necessary library support. */ -std::optional SanityChecks(const Context&); +[[nodiscard]] std::optional SanityChecks(const Context&); -} +} // namespace kernel #endif // BITCOIN_KERNEL_CHECKS_H diff --git a/src/node/blockmanager_args.h b/src/node/blockmanager_args.h index e657c6bb4568..a3e04916d105 100644 --- a/src/node/blockmanager_args.h +++ b/src/node/blockmanager_args.h @@ -14,7 +14,7 @@ class ArgsManager; struct bilingual_str; namespace node { -std::optional ApplyArgsManOptions(const ArgsManager& args, BlockManager::Options& opts); +[[nodiscard]] std::optional ApplyArgsManOptions(const ArgsManager& args, BlockManager::Options& opts); } // namespace node #endif // BITCOIN_NODE_BLOCKMANAGER_ARGS_H diff --git a/src/node/chainstatemanager_args.h b/src/node/chainstatemanager_args.h index 6c46b998f213..dd6771f5232d 100644 --- a/src/node/chainstatemanager_args.h +++ b/src/node/chainstatemanager_args.h @@ -13,7 +13,7 @@ class ArgsManager; struct bilingual_str; namespace node { -std::optional ApplyArgsManOptions(const ArgsManager& args, ChainstateManager::Options& opts); +[[nodiscard]] std::optional ApplyArgsManOptions(const ArgsManager& args, ChainstateManager::Options& opts); } // namespace node #endif // BITCOIN_NODE_CHAINSTATEMANAGER_ARGS_H diff --git a/src/script/descriptor.cpp b/src/script/descriptor.cpp index 95f87d4d13f6..11378ac203a3 100644 --- a/src/script/descriptor.cpp +++ b/src/script/descriptor.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include diff --git a/src/script/miniscript.cpp b/src/script/miniscript.cpp index 5e471cbe89e8..3937638cf80a 100644 --- a/src/script/miniscript.cpp +++ b/src/script/miniscript.cpp @@ -172,8 +172,8 @@ Type ComputeType(Fragment fragment, Type x, Type y, Type z, const std::vector::max(); + has_sig = false; + malleable = false; + non_canon = false; + } + return *this; +} + +InputStack& InputStack::SetWithSig() { + has_sig = true; + return *this; +} + +InputStack& InputStack::SetNonCanon() { + non_canon = true; + return *this; +} + +InputStack& InputStack::SetMalleable(bool x) { + malleable = x; + return *this; +} + +InputStack operator+(InputStack a, InputStack b) { + a.stack = Cat(std::move(a.stack), std::move(b.stack)); + if (a.available != Availability::NO && b.available != Availability::NO) a.size += b.size; + a.has_sig |= b.has_sig; + a.malleable |= b.malleable; + a.non_canon |= b.non_canon; + if (a.available == Availability::NO || b.available == Availability::NO) { + a.SetAvailable(Availability::NO); + } else if (a.available == Availability::MAYBE || b.available == Availability::MAYBE) { + a.SetAvailable(Availability::MAYBE); + } + return a; +} + +InputStack operator|(InputStack a, InputStack b) { + // If only one is invalid, pick the other one. If both are invalid, pick an arbitrary one. + if (a.available == Availability::NO) return b; + if (b.available == Availability::NO) return a; + // If only one of the solutions has a signature, we must pick the other one. + if (!a.has_sig && b.has_sig) return a; + if (!b.has_sig && a.has_sig) return b; + if (!a.has_sig && !b.has_sig) { + // If neither solution requires a signature, the result is inevitably malleable. + a.malleable = true; + b.malleable = true; + } else { + // If both options require a signature, prefer the non-malleable one. + if (b.malleable && !a.malleable) return a; + if (a.malleable && !b.malleable) return b; + } + // Between two malleable or two non-malleable solutions, pick the smaller one between + // YESes, and the bigger ones between MAYBEs. Prefer YES over MAYBE. + if (a.available == Availability::YES && b.available == Availability::YES) { + return std::move(a.size <= b.size ? a : b); + } else if (a.available == Availability::MAYBE && b.available == Availability::MAYBE) { + return std::move(a.size >= b.size ? a : b); + } else if (a.available == Availability::YES) { + return a; + } else { + return b; + } +} + std::optional> DecomposeScript(const CScript& script) { std::vector out; diff --git a/src/script/miniscript.h b/src/script/miniscript.h index 21755d76838c..3bf562e1cff3 100644 --- a/src/script/miniscript.h +++ b/src/script/miniscript.h @@ -223,6 +223,11 @@ enum class Fragment { // WRAP_U(X) is represented as OR_I(X,0) }; +enum class Availability { + NO, + YES, + MAYBE, +}; namespace internal { @@ -235,6 +240,62 @@ size_t ComputeScriptLen(Fragment fragment, Type sub0typ, size_t subsize, uint32_ //! A helper sanitizer/checker for the output of CalcType. Type SanitizeType(Type x); +//! An object representing a sequence of witness stack elements. +struct InputStack { + /** Whether this stack is valid for its intended purpose (satisfaction or dissatisfaction of a Node). + * The MAYBE value is used for size estimation, when keys/preimages may actually be unavailable, + * but may be available at signing time. This makes the InputStack structure and signing logic, + * filled with dummy signatures/preimages usable for witness size estimation. + */ + Availability available = Availability::YES; + //! Whether this stack contains a digital signature. + bool has_sig = false; + //! Whether this stack is malleable (can be turned into an equally valid other stack by a third party). + bool malleable = false; + //! Whether this stack is non-canonical (using a construction known to be unnecessary for satisfaction). + //! Note that this flag does not affect the satisfaction algorithm; it is only used for sanity checking. + bool non_canon = false; + //! Serialized witness size. + size_t size = 0; + //! Data elements. + std::vector> stack; + //! Construct an empty stack (valid). + InputStack() {} + //! Construct a valid single-element stack (with an element up to 75 bytes). + InputStack(std::vector in) : size(in.size() + 1), stack(Vector(std::move(in))) {} + //! Change availability + InputStack& SetAvailable(Availability avail); + //! Mark this input stack as having a signature. + InputStack& SetWithSig(); + //! Mark this input stack as non-canonical (known to not be necessary in non-malleable satisfactions). + InputStack& SetNonCanon(); + //! Mark this input stack as malleable. + InputStack& SetMalleable(bool x = true); + //! Concatenate two input stacks. + friend InputStack operator+(InputStack a, InputStack b); + //! Choose between two potential input stacks. + friend InputStack operator|(InputStack a, InputStack b); +}; + +/** A stack consisting of a single zero-length element (interpreted as 0 by the script interpreter in numeric context). */ +static const auto ZERO = InputStack(std::vector()); +/** A stack consisting of a single malleable 32-byte 0x0000...0000 element (for dissatisfying hash challenges). */ +static const auto ZERO32 = InputStack(std::vector(32, 0)).SetMalleable(); +/** A stack consisting of a single 0x01 element (interpreted as 1 by the script interpreted in numeric context). */ +static const auto ONE = InputStack(Vector((unsigned char)1)); +/** The empty stack. */ +static const auto EMPTY = InputStack(); +/** A stack representing the lack of any (dis)satisfactions. */ +static const auto INVALID = InputStack().SetAvailable(Availability::NO); + +//! A pair of a satisfaction and a dissatisfaction InputStack. +struct InputResult { + InputStack nsat, sat; + + template + InputResult(A&& in_nsat, B&& in_sat) : nsat(std::forward(in_nsat)), sat(std::forward(in_sat)) {} +}; + //! Class whose objects represent the maximum of a list of integers. template struct MaxInt { @@ -803,14 +864,26 @@ struct Node { size_t ScriptSize() const { return scriptlen; } //! Return the maximum number of ops needed to satisfy this script non-malleably. - uint32_t GetOps() const { return ops.count + ops.sat.value; } + std::optional GetOps() const { + if (!ops.sat.valid) return {}; + return ops.count + ops.sat.value; + } //! Check the ops limit of this script against the consensus limit. - bool CheckOpsLimit() const { return GetOps() <= MAX_OPS_PER_SCRIPT; } + bool CheckOpsLimit() const { + if (const auto ops = GetOps()) return *ops <= MAX_OPS_PER_SCRIPT; + return true; + } /** Return the maximum number of stack elements needed to satisfy this script non-malleably, including * the script push. */ - uint32_t GetStackSize() const { return ss.sat.value + 1; } + std::optional GetStackSize() const { + if (!ss.sat.valid) return {}; + return ss.sat.value + 1; + } + + //! Whether no satisfaction exists for this node. + bool IsNotSatisfiable() const { return !GetStackSize(); } //! Return the expression type. Type GetType() const { return typ; } @@ -842,6 +915,18 @@ struct Node { //! Check whether this node is safe as a script on its own. bool IsSane() const { return IsValidTopLevel() && IsSaneSubexpression() && NeedsSignature(); } + //! Produce a witness for this script, if possible and given the information available in the context. + //! The non-malleable satisfaction is guaranteed to be valid if it exists, and ValidSatisfaction() + //! is true. If IsSane() holds, this satisfaction is guaranteed to succeed in case the node's + //! conditions are satisfied (private keys and hash preimages available, locktimes satsified). + template + Availability Satisfy(const Ctx& ctx, std::vector>& stack, bool nonmalleable = true) const { + auto ret = ProduceInput(ctx); + if (nonmalleable && (ret.sat.malleable || !ret.sat.has_sig)) return Availability::NO; + stack = std::move(ret.sat.stack); + return ret.sat.available; + } + //! Equality testing. bool operator==(const Node& arg) const { return Compare(*this, arg) == 0; } diff --git a/src/script/sign.cpp b/src/script/sign.cpp index 8df3235a24d8..3993702e839b 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -10,6 +10,7 @@ #include #include #include