Summary
compileMiniscript() throws RangeError: Maximum call stack size exceeded on a deeply nested but well-formed miniscript expression.
In local testing, parsing succeeds first; the overflow happens later in the analysis pass, specifically in analyzeNode, which appears to recurse through the AST without a depth bound.
Verified on @bitcoinerlab/miniscript v2.0.0 under Node v24.
Reproduction
const { compileMiniscript } = require('@bitcoinerlab/miniscript');
let ms = 'older(1)';
for (let i = 0; i < 1600; i++) {
ms = `and_v(v:older(1),${ms})`;
}
ms = 'j:' + ms;
compileMiniscript(ms);
Observed behavior
The call throws:
RangeError: Maximum call stack size exceeded
at analyzeNode (.../dist/compiler/analyze.js)
at analyzeNode (...)
...
I verified this locally with n = 1600 and n = 3000. The input is deeply nested and well-formed, and parsing succeeds before the failure in analysis.
Why this seems to happen
The failure appears to come from recursive descent in analyzeNode over wrappers and child nodes without a nesting limit.
From local source review, the relevant logic is in src/compiler/analyze.ts around the main analyzeNode implementation and its recursive calls for wrappers and binary fragments.
Impact
This is an availability / robustness issue in the public compileMiniscript() API.
If an application compiles attacker-controlled or otherwise untrusted miniscript input, a deeply nested expression can trigger an unexpected raw RangeError instead of a normal validation error.
Expected behavior
A deeply nested miniscript should be rejected gracefully with a normal compile/validation error instead of overflowing the JavaScript call stack.
Summary
compileMiniscript()throwsRangeError: Maximum call stack size exceededon a deeply nested but well-formed miniscript expression.In local testing, parsing succeeds first; the overflow happens later in the analysis pass, specifically in
analyzeNode, which appears to recurse through the AST without a depth bound.Verified on
@bitcoinerlab/miniscriptv2.0.0 under Node v24.Reproduction
Observed behavior
The call throws:
I verified this locally with
n = 1600andn = 3000. The input is deeply nested and well-formed, and parsing succeeds before the failure in analysis.Why this seems to happen
The failure appears to come from recursive descent in
analyzeNodeover wrappers and child nodes without a nesting limit.From local source review, the relevant logic is in
src/compiler/analyze.tsaround the mainanalyzeNodeimplementation and its recursive calls for wrappers and binary fragments.Impact
This is an availability / robustness issue in the public
compileMiniscript()API.If an application compiles attacker-controlled or otherwise untrusted miniscript input, a deeply nested expression can trigger an unexpected raw
RangeErrorinstead of a normal validation error.Expected behavior
A deeply nested miniscript should be rejected gracefully with a normal compile/validation error instead of overflowing the JavaScript call stack.