Repository navigation
backport: bitcoin#24149, #27200, #27733, #27774, #27876, #27997, #28009, #28987 #8076
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from all commits
ff3b4cc
774a9a8
f863b45
6adb460
57cc86f
6dc0079
1a11c22
ee3cdf0
449d6a1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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<std::vector<unsigned char>> stack; | ||
| //! Construct an empty stack (valid). | ||
| InputStack() {} | ||
| //! Construct a valid single-element stack (with an element up to 75 bytes). | ||
| InputStack(std::vector<unsigned char> 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<unsigned char>()); | ||
| /** A stack consisting of a single malleable 32-byte 0x0000...0000 element (for dissatisfying hash challenges). */ | ||
| static const auto ZERO32 = InputStack(std::vector<unsigned char>(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<typename A, typename B> | ||
| InputResult(A&& in_nsat, B&& in_sat) : nsat(std::forward<A>(in_nsat)), sat(std::forward<B>(in_sat)) {} | ||
| }; | ||
|
|
||
| //! Class whose objects represent the maximum of a list of integers. | ||
| template<typename I> | ||
| 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<uint32_t> 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<uint32_t> 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<typename Ctx> | ||
| Availability Satisfy(const Ctx& ctx, std::vector<std::vector<unsigned char>>& stack, bool nonmalleable = true) const { | ||
| auto ret = ProduceInput(ctx); | ||
|
Comment on lines
+923
to
+924
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| if (nonmalleable && (ret.sat.malleable || !ret.sat.has_sig)) return Availability::NO; | ||
| stack = std::move(ret.sat.stack); | ||
| return ret.sat.available; | ||
|
Comment on lines
+922
to
+927
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 Suggestion: Do not retain a satisfaction API whose implementation is absent The partial bitcoin#24149 pick adds Satisfy(), but its ProduceInput(ctx) call has no implementation anywhere in Dash. Upstream 22c5b00 supplies that algorithm using the single-argument TreeEval overload from d25d58b; neither is present here. Instantiating the new method with the retained Satisfier or another ordinary satisfaction context therefore fails to compile. There are no current callers, so this is not a claim that existing source builds fail. The declared partial scope justifies excluding unsupported signing functionality, but the retained API should be coherent: either include its implementation and evaluation helper or remove Satisfy() until satisfaction support is available. source: backport-reviewer |
||
| } | ||
|
|
||
| //! Equality testing. | ||
| bool operator==(const Node<Key>& arg) const { return Compare(*this, arg) == 0; } | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔴 Blocking: Account for the missing descriptor portion of bitcoin#27997
The PR and commit a4e4f03 claim the complete bitcoin#27997 descriptor-rejection change, but this pick only adapts resource accessors, adds IsNotSatisfiable(), and adds an include. Upstream 639e3b6 changes ParseScript() to reject unsatisfiable Miniscripts, c7db88a adds the nonempty-key assertion, and a49402a adds the import regression. All three transformations are absent because Dash lacks the Miniscript descriptor parser; git log -S'MiniscriptDescriptor' finds no such implementation in the base. The declared partial scope applies to bitcoin#24149, not bitcoin#27997. Either carry the required descriptor prerequisites and transformations, or explicitly mark this pick partial and document the retained resource-accessor preparation and excluded descriptor work. This is a completeness/history issue, not a claim that Dash currently accepts those unsupported descriptors.
source: backport-reviewer; dash-core-commit-history