Skip to content

Conversation

@hodlinator
Copy link
Contributor

@hodlinator hodlinator commented Jan 22, 2025

Removes one level of unnecessary indirection, which was a change that originally aided in finding one issue in #30866. Simplifies the code one step further than 09a1875 belonging to aforementioned PR.

Also adds test which verifies resistance to stack overflow when it comes to ~Node() and Node::Clone().

No observed difference when running benchmarks: ExpandDescriptor/WalletIsMineDescriptors/WalletIsMineMigratedDescriptors/WalletLoadingDescriptors.

Followup to #30866.

@DrahtBot
Copy link
Contributor

DrahtBot commented Jan 22, 2025

The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

Code Coverage & Benchmarks

For details see: https://corecheck.dev/bitcoin/bitcoin/pulls/31713.

Reviews

See the guideline for information on the review process.

Type Reviewers
ACK l0rinc
Stale ACK sipa

If your review is incorrectly listed, please copy-paste <!--meta-tag:bot-skip--> into the comment that the bot should ignore.

Conflicts

Reviewers, this pull request conflicts with the following ones:

  • #34141 (miniscript: Use Func and Expr when parsing keys, hashes, and locktimes by achow101)
  • #32471 (wallet/rpc: fix listdescriptors RPC fails to return descriptors with private key information when wallet contains descriptors missing any key by Eunovo)

If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.

LLM Linter (✨ experimental)

Possible places where named args for integral literals may be used (e.g. func(x, /*named_arg=*/0) in C++, and func(x, named_arg=0) in Python):

  • node.Satisfy(satisfier, witness_mal.stack, false) in src/test/fuzz/miniscript.cpp
  • node.Satisfy(satisfier, witness_nonmal.stack, true) in src/test/fuzz/miniscript.cpp
  • node.Satisfy(satisfier, witness_mal.stack, false) in src/test/miniscript_tests.cpp
  • node.Satisfy(satisfier, witness_nonmal.stack, true) in src/test/miniscript_tests.cpp

2026-01-03

@hodlinator
Copy link
Contributor Author

CC @achow101 @darosior @brunoerg from parent PR to gauge their interest.

It's unfortunate that this PR touches many lines, so if there's little support for the refactoring I'm fine with closing, or letting it linger for a while (I understand this is super-low priority). At least it helped find the issue mentioned at the top of the summary.

@darosior
Copy link
Member

CI failure looks unrelated. Could you s/script/miniscript/ in the PR title to make it clear to reviewer this does not touch consensus critical stuff?

It's unfortunate that this PR touches many lines

It's also not a part of the codebase where i expect much churn, so it probably lowers the bar for doing something like this.

@hodlinator hodlinator changed the title script refactor: Remove superfluous unique_ptr-indirection (#30866 follow-up) miniscript refactor: Remove unique_ptr-indirection (#30866 follow-up) Jan 23, 2025
@hodlinator hodlinator force-pushed the 2024/11/miniscript_ownership branch from ed9e9fb to 58e9c7c Compare January 23, 2025 07:51
@maflcko
Copy link
Member

maflcko commented Jan 23, 2025

CI failure looks unrelated.

I can't seem to find it. It would be good to always report or at least leave a comment with the log to any CI failure. Otherwise, it will remain unfixed and will just happen on another pull later on.

@l0rinc
Copy link
Contributor

l0rinc commented Jan 23, 2025

leave a comment with the log to any CI failure

I think they meant the p2p_1p1c_network failure at https://github.com/bitcoin/bitcoin/actions/runs/12917231673/job/36023158007?pr=31713

@hodlinator
Copy link
Contributor Author

I think they meant the p2p_1p1c_network failure at https://github.com/bitcoin/bitcoin/actions/runs/12917231673/job/36023158007?pr=31713

Yes, that's the one, thanks for finding it @l0rinc!

Could you s/script/miniscript/ in the PR title to make it clear to reviewer this does not touch consensus critical stuff?

Updated PR title and commit message titles.

@darosior
Copy link
Member

From a quick skim, this looks fine to me. I wonder if @sipa has any objection to not using pointers here.

Let me see if we can get the conflicts in first and then do this when nothing else is conflicting anymore.

@sipa
Copy link
Member

sipa commented Jan 23, 2025

Very rough-skim Concept ACK.

This will make the code diverge further from the c++ miniscript policy compiler code, but that's a bullet we already took with the shared_ptr to unique_ptr change, and it doesn't seem there is development happening on that anyway.

@l0rinc
Copy link
Contributor

l0rinc commented Nov 19, 2025

Redid the rebase locally, besides an import collision the following was added:

diff --git a/src/test/miniscript_tests.cpp b/src/test/miniscript_tests.cpp
--- a/src/test/miniscript_tests.cpp	(revision 31196b79cabb6e4519e8a27165e1be75c5044403)
+++ b/src/test/miniscript_tests.cpp	(date 1763547689484)
@@ -727,7 +727,7 @@
     g_testdata.reset();
 }
 
-// Confirm that ~Node() and Node::Clone() are stack-safe.
+// Confirm that ~Node(), Node::Clone() and operator=(Node&&) are stack-safe.
 BOOST_AUTO_TEST_CASE(node_deep_destruct)
 {
     using miniscript::internal::NoDupCheck;
@@ -744,6 +744,8 @@
 
     auto clone{root.Clone()};
     BOOST_CHECK_EQUAL(clone.ScriptSize(), root.ScriptSize());
+
+    clone = std::move(root);
 }
 
 BOOST_AUTO_TEST_SUITE_END()

Code coverage indicates both move method are tested now:
https://github.com/bitcoin/bitcoin/blob/c6ac239124b3e66f2d5ff52edc6ff422926abead/src/script/miniscript.h#L1716-L1717

reACK c6ac239124b3e66f2d5ff52edc6ff422926abead

@hodlinator hodlinator force-pushed the 2024/11/miniscript_ownership branch from c6ac239 to d782bff Compare November 21, 2025 15:33
@l0rinc
Copy link
Contributor

l0rinc commented Nov 21, 2025

reACK d782bff

The only difference since last ACK was to omit collecting empty leaves in the destructor

@fanquake fanquake changed the title miniscript refactor: Remove unique_ptr-indirection (#30866 follow-up) miniscript refactor: Remove unique_ptr-indirection Dec 4, 2025
Copy link
Member

@sipa sipa left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reACK d782bff

@fanquake
Copy link
Member

fanquake commented Dec 9, 2025

@darosior conceptual / final look?

@darosior
Copy link
Member

darosior commented Dec 9, 2025

Looks good conceptually. I may get to it later but it's not a priority for me so i don't want to be a blocker.

@l0rinc
Copy link
Contributor

l0rinc commented Dec 18, 2025

Sorry for the conflict - happy to re-review after the rebase. Tried it locally, seems trivial.

@hodlinator hodlinator force-pushed the 2024/11/miniscript_ownership branch from d782bff to 7909f9f Compare December 18, 2025 15:36
@hodlinator
Copy link
Contributor Author

Rebased to solve trivial conflict with #33192.

@l0rinc
Copy link
Contributor

l0rinc commented Dec 18, 2025

Rebased locally, soft reset against re-rebased PR HEAD is empty.

ACK 7909f9f

hodlinator and others added 7 commits January 3, 2026 12:52
Correct destructor implementation comment to no longer refer to shared pointers and also move it into the function body, in symmetry with Clone() right below.

Leftover from bitcoin#30866.
Makes a lot of fields in miniscript.h non-const in order to allow move-operations 2 commits later.

Also fixes adjacent comment typos.

Co-authored-by: Lőrinc <[email protected]>
Co-authored-by: MarcoFalke <*~=`'#}+{/-|&$^[email protected]>
Functional parity is achieved through making Node move-able.

Unfortunately ~Node() now needs to have the recursion linter disabled, as it is unable to figure out that recursion stops 1 level down. The former smart pointers must have been circumventing the linter somehow.

NodeRef & MakeNodeRef() are deleted in the following commit (broken out to facilitate review).
(Also removes parameter to TestSatisfy() which existed unused from the start in 22c5b00).
Can be tested through emptying the function body of ~Node() or replacing Clone() implementation with naive version:
```C++
Node<Key> Clone() const
{
    std::vector<Node> new_subs;
    new_subs.reserve(subs.size());
    for (const Node& child : subs) {
        new_subs.push_back(child.Clone());
    }
    return Node{internal::NoDupCheck{}, m_script_ctx, fragment, std::move(new_subs), keys, data, k};
}
```

Co-authored-by: Lőrinc <[email protected]>
@hodlinator hodlinator force-pushed the 2024/11/miniscript_ownership branch from 7909f9f to bdc8802 Compare January 3, 2026 12:09
@hodlinator
Copy link
Contributor Author

Rebased to resolve conflict with #33135.

@l0rinc
Copy link
Contributor

l0rinc commented Jan 3, 2026

Re-rebased and soft reset the results and the small remaining diff seem good to me.

Details
diff --git a/src/script/descriptor.cpp b/src/script/descriptor.cpp
index df21f6cdb9..dedc35afc8 100644
--- a/src/script/descriptor.cpp
+++ b/src/script/descriptor.cpp
@@ -1552,9 +1552,9 @@ public:
         : DescriptorImpl(std::move(providers), "?"), m_node(std::move(node))
     {
         // Traverse miniscript tree for unsafe use of older()
-        miniscript::ForEachNode(*m_node, [&](const miniscript::Node<uint32_t>& node) {
-            if (node.fragment == miniscript::Fragment::OLDER) {
-                const uint32_t raw = node.k;
+        miniscript::ForEachNode(m_node, [&](const miniscript::Node<uint32_t>& node) {
+            if (node.Fragment() == miniscript::Fragment::OLDER) {
+                const uint32_t raw = node.K();
                 const uint32_t value_part = raw & ~CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG;
                 if (value_part > CTxIn::SEQUENCE_LOCKTIME_MASK) {
                     const bool is_time_based = (raw & CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG) != 0;
diff --git a/src/script/miniscript.h b/src/script/miniscript.h
index 9d5ebcfd87..bd3a16f404 100644
--- a/src/script/miniscript.h
+++ b/src/script/miniscript.h
@@ -191,11 +191,6 @@ inline consteval Type operator""_mst(const char* c, size_t l)
 using Opcode = std::pair<opcodetype, std::vector<unsigned char>>;
 
 template<typename Key> class Node;
-template<typename Key> using NodeRef = Node<Key>; // <- TODO: Remove in next commit.
-
-//! Construct a miniscript node (TODO: remove in next commit).
-template<typename Key, typename... Args>
-Node<Key> MakeNodeRef(Args&&... args) { return Node<Key>(std::forward<Args>(args)...); }
 
 //! Unordered traversal of a miniscript node tree.
 template <typename Key, std::invocable<const Node<Key>&> Fn>
@@ -206,8 +201,8 @@ void ForEachNode(const Node<Key>& root, Fn&& fn)
         const Node<Key>& node = stack.back();
         std::invoke(fn, node);
         stack.pop_back();
-        for (const auto& sub : node.subs) {
-            stack.emplace_back(*sub);
+        for (const auto& sub : node.Subs()) {
+            stack.emplace_back(sub);
         }
     }
 }

Also verified with git range-diff -w -U0 master 7909f9f bdc8802 to be sure.
Someone with more expertise in this area should also check it.

code review ACK bdc8802

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants