{
 "number": 36028,
 "repo": "bitcoin/bitcoin",
 "url": "https://github.com/bitcoin/bitcoin/pull/36028",
 "title": "miniscript: make typing rule match BIP-379 exactly",
 "author": "sipa",
 "author_association": "MEMBER",
 "created_at": "2026-08-19T20:42:37Z",
 "updated_at": "2026-08-26T07:22:36Z",
 "age_days": 28,
 "draft": true,
 "labels": [
  "Descriptors"
 ],
 "milestone": null,
 "base": "master",
 "head_sha": "2631ece7b518a5f45eb397960eea81441374d514",
 "head_ref": "202608_miniscript_bip_rules",
 "head_repo": "sipa/bitcoin",
 "head_history": [],
 "additions": 4,
 "deletions": 6,
 "changed_files": 1,
 "commit_count": 1,
 "size_bucket": "S",
 "mergeable_state": "blocked",
 "bot": {
  "drahtbot": {
   "present": true,
   "reviews": {
    "concept_ack": [
     {
      "login": "guggero",
      "url": "https://github.com/bitcoin/bitcoin/pull/36028#pullrequestreview-5000252711"
     }
    ]
   },
   "conflicts": []
  }
 },
 "acks_parsed": {
  "guggero": {
   "kind": "concept_ack",
   "hash": "2631ece",
   "t": "2026-08-22T13:26:21Z",
   "stale": false
  }
 },
 "acks_tally": {
  "ack": 0,
  "stale_ack": 0,
  "concept_ack": 1,
  "approach_ack": 0,
  "nack": 0,
  "concept_nack": 0,
  "approach_nack": 0
 },
 "reviews": {
  "approved": 0,
  "changes_requested": 0,
  "distinct_reviewers": [
   "guggero"
  ]
 },
 "signals": {
  "needs_rebase": false,
  "ci_failed": false,
  "mergeable_state": "blocked",
  "last_author_activity": "2026-08-19T20:35:16Z",
  "last_reviewer_activity": "2026-08-26T07:22:36Z",
  "last_reviewer": "guggero",
  "author_silent_days": 28,
  "waiting_on_author_days": 22,
  "days_since_update": 22
 },
 "refs": {
  "mentioned": [],
  "depends_on": [],
  "fixes": [],
  "linked_issues": [],
  "references": [],
  "conflicts": []
 },
 "stack": {
  "shares_commits_with": [],
  "based_on": [],
  "base_for": []
 },
 "review_paths": [
  "src/script/miniscript.cpp"
 ],
 "body": "The [BIP-379](https://github.com/bitcoin/bips/blob/master/bip-0379.md) text specifies rule for reasoning about correctness and malleability through a number of type properties. These rules are implemented in Bitcoin Core's miniscript implementation.\n\nThere are multiple equivalent ways of writing these. For example, the BIP text says that an `or_b` fragment always has the \"e\" (uniquely dissatisfiable) property. The Bitcoin Core implementation however only assigns it the \"e\" property when both child expressions have \"e\" as well. This is an irrelevant distinction, because the \"e\" property is only used for reasoning about malleability, and an `or_b` whose children do not have \"e\" is always malleable. Thus, for all cases where it matters, \"always e\" and \"e when children have e\" are equivalent.\n\nIn the context of https://github.com/bitcoin/bips/pull/2240, which adds test vectors to BIP-379, it is helpful however to make the implementation match the specification exactly, even for these cases where it does not matter.\n\nDraft until these is some agreement on this approach in https://github.com/bitcoin/bips/pull/2240.",
 "commits": [
  {
   "sha": "2631ece7b518a5f45eb397960eea81441374d514",
   "date": "2026-08-19T20:35:16Z",
   "message": "miniscript: make typing rule match BIP-379 exactly"
  }
 ],
 "timeline": [
  {
   "t": "2026-08-22T13:18:58Z",
   "kind": "review_comment",
   "who": "guggero",
   "assoc": "CONTRIBUTOR",
   "path": "src/script/miniscript.cpp",
   "commit": "2631ece7b518a5f45eb397960eea81441374d514",
   "in_reply_to": null,
   "text": "These two deleted lines were no-ops in the first place, which may be worth stating for\nreviewers who want to convince themselves the change is behavior-preserving:\n\n- `d:` `o=z_x` -> `o`: `d:` requires its child to be `Vz`, so `z_x` always holds.\n- `d:` `e=f_x` -> `e`: every `V` expression has \"f\". By induction \u2014 `v:` and\n  `or_c` are unconditionally \"f\"; `and_v(X,Y)` has `f=s_X+f_Y` with `Y` a `V`;\n  `andor(X,Y,Z)` has `f=f_Z(s_X+f_Y)` with `Y`,`Z` both `V`; `or_i(X,Z)` has\n  `f=f_X f_Z` with both `V`."
  },
  {
   "t": "2026-08-22T13:25:00Z",
   "kind": "review_comment",
   "who": "guggero",
   "assoc": "CONTRIBUTOR",
   "path": "src/script/miniscript.cpp",
   "commit": "2631ece7b518a5f45eb397960eea81441374d514",
   "in_reply_to": null,
   "text": "More context for other reviewers: This is the only hunk with an observable delta, and it cannot change what\nCore accepts: \"m\" still requires every child to be \"e\", and wherever an \"m\" rule\nconsumes a child's \"e\" it also requires that child's \"m\", so no \"m\" value moves\nand descriptor acceptance is unchanged. Only the type of an already-malleable\nexpression differs. The [vector set](https://github.com/bitcoin/bips/pull/2240) does discriminate (40 of the 36,370 lines)\nwhich is how this surfaced.\n\nOne thing you may want to point at from here: after this PR, Core assigns \"e\" to\nexpressions whose dissatisfaction is provably not unique, which contradicts the\nBIP's own description of the property (\"this requires a unique unconditional\ndissatisfaction to exist\"). `or_i(pk(A),pk(B))` is a legal `thresh` child that is\n\"s\" but not \"e\", and spending\n\n```\nor_d(thresh(2,or_i(pk(A),pk(B)),a:or_i(pk(C),pk(D))),pk(E))\n```\n\nthrough its second branch has four consensus-valid witnesses that differ only in\nthe two branch-selector bytes, none of them needing a signature from A..D. The\nthreshold is malleable either way, so nothing is wrong with the rule, but the\ndescription reads as a guarantee. bitcoin/bips#2267 adds a sentence saying the\ndescriptions only bind for expressions that meet the malleability requirements,\nwhich would keep this PR from looking like it contradicts the spec it is\nmatching."
  },
  {
   "t": "2026-08-22T13:26:21Z",
   "kind": "review",
   "who": "guggero",
   "assoc": "CONTRIBUTOR",
   "state": "COMMENTED",
   "commit": "2631ece7b518a5f45eb397960eea81441374d514",
   "text": "Concept ACK 2631ece.\n\nI agree on the approach and the Go implementation in btcsuite/btcd#2568\nand the [test vector BIP PR](https://github.com/bitcoin/bips/pull/2240) are now updated.\nAssuming the [`rust-miniscript` PR](https://github.com/rust-bitcoin/rust-miniscript/pull/1032)\nis also accepted and merged, the three implementations would then be aligned."
  },
  {
   "t": "2026-08-26T07:22:36Z",
   "kind": "comment",
   "who": "guggero",
   "assoc": "CONTRIBUTOR",
   "text": "After the discussion in https://github.com/bitcoin/bips/pull/2267 and the new approach proposed in https://github.com/rust-bitcoin/rust-miniscript/pull/1037, I took the liberty of instructing an LLM to implement the same changes on top of this PR. Feel free to take whatever you find useful from this commit (assuming this is the direction that finds agreement): https://github.com/guggero/bitcoin/commit/576eb5f3252bd4159c302ae2bfe70b6ae71f6e33"
  }
 ],
 "labels_log": [
  {
   "t": "2026-08-19T20:42:41Z",
   "action": "labeled",
   "label": "Descriptors",
   "who": "DrahtBot"
  }
 ],
 "state_log": [],
 "text_chars": 3937,
 "text_tokens_estimate": 984,
 "changed_paths": [
  "src/script/miniscript.cpp"
 ],
 "files": [
  {
   "path": "src/script/miniscript.cpp",
   "add": 4,
   "del": 6
  }
 ],
 "test_lines": 0,
 "git": {
  "head": "2631ece7b518a5f45eb397960eea81441374d514",
  "head_matches_backup": true,
  "base": "4d86d9cc7ed0b623170a78986c57aa75ba6a46c0",
  "commits": [
   {
    "sha": "2631ece7b5",
    "subject": "miniscript: make typing rule match BIP-379 exactly",
    "files": 1,
    "add": 4,
    "del": 6
   }
  ],
  "patch_truncated": false
 },
 "input_hash": "34757ed87f9f4044",
 "extracted_at": "2026-09-17T16:15:31+00:00"
}