@bitcoinerlab/miniscript-policies 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +138 -10
- package/dist/index.js +63 -47
- package/package.json +6 -1
- package/types/index.d.ts +7 -4
- package/Makefile +0 -14
- package/example.js +0 -34
- package/js_bindings.cpp +0 -154
- package/patches/uppercase-h.patch +0 -11
- package/rollup.config.js +0 -13
- package/src/index.js +0 -5
- package/src/miniscript.js +0 -149
- package/test/fixtures.js +0 -898
- package/test/miniscript.test.js +0 -21
- package/test/policy.test.js +0 -20
package/README.md
CHANGED
|
@@ -1,26 +1,154 @@
|
|
|
1
1
|
# Miniscript Policies
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
`@bitcoinerlab/miniscript-policies` exposes the reference C++ Miniscript policy
|
|
4
|
+
compiler from [`sipa/miniscript`](https://github.com/sipa/miniscript) in
|
|
5
|
+
JavaScript via Emscripten.
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
It is designed for projects that want to compile human-friendly policies (like
|
|
8
|
+
`or(and(pk(A),older(8640)),pk(B))`) into Miniscript expressions.
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
Upstream Miniscript reference docs: https://bitcoin.sipa.be/miniscript/
|
|
11
|
+
|
|
12
|
+
## What this package provides
|
|
13
|
+
|
|
14
|
+
- `compilePolicy(policy)`: reference policy compiler output (P2WSH-oriented)
|
|
15
|
+
- `compilePolicyTaproot(policy)`: Taproot-focused policy compiler shim
|
|
16
|
+
- `ready`: promise you must await before using compiler functions
|
|
17
|
+
|
|
18
|
+
For Miniscript parsing/analysis/satisfier features, use
|
|
19
|
+
[`@bitcoinerlab/miniscript`](https://github.com/bitcoinerlab/miniscript).
|
|
20
|
+
|
|
21
|
+
## How Taproot support works
|
|
22
|
+
|
|
23
|
+
`compilePolicyTaproot` is a strict shim built on top of the same reference
|
|
24
|
+
compiler:
|
|
25
|
+
|
|
26
|
+
1. Compile policy with the reference C++ compiler.
|
|
27
|
+
2. Rewrite `multi(...)` to `multi_a(...)`.
|
|
28
|
+
3. Validate the rewritten expression in tapscript context using
|
|
29
|
+
`@bitcoinerlab/miniscript`.
|
|
30
|
+
4. If reference compilation fails, forward the same reference error string
|
|
31
|
+
(for example `"[compile error]"` or `"[exception: ...]"`) with
|
|
32
|
+
`issane=false`.
|
|
33
|
+
5. If rewrite/validation fails, return a Taproot-specific error string with
|
|
34
|
+
prefix `"[taproot rewrite error]"` (or `"[taproot rewrite exception]"`)
|
|
35
|
+
and `issane=false`.
|
|
36
|
+
|
|
37
|
+
This gives a practical policy-to-miniscript path for Taproot while preserving
|
|
38
|
+
strict safety checks.
|
|
39
|
+
|
|
40
|
+
## Installation
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
npm install @bitcoinerlab/miniscript-policies
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Quick start
|
|
11
47
|
|
|
12
48
|
```javascript
|
|
13
|
-
import {
|
|
49
|
+
import {
|
|
50
|
+
compilePolicy,
|
|
51
|
+
compilePolicyTaproot,
|
|
52
|
+
ready
|
|
53
|
+
} from '@bitcoinerlab/miniscript-policies';
|
|
14
54
|
|
|
15
55
|
await ready;
|
|
56
|
+
|
|
16
57
|
const policy = 'or(and(pk(A),older(8640)),pk(B))';
|
|
17
|
-
|
|
18
|
-
|
|
58
|
+
|
|
59
|
+
// Reference compiler (includes asm)
|
|
60
|
+
const p2wsh = compilePolicy(policy);
|
|
61
|
+
if (!p2wsh.issane) {
|
|
62
|
+
console.warn('Policy result is not sane for P2WSH:', p2wsh.miniscript);
|
|
63
|
+
} else {
|
|
64
|
+
console.log(p2wsh.miniscript);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Taproot shim (no asm)
|
|
68
|
+
const taproot = compilePolicyTaproot('thresh(2,pk(A),pk(B),pk(C))');
|
|
69
|
+
if (!taproot.issane) {
|
|
70
|
+
console.warn('Policy result is not sane for tapscript:', taproot.miniscript);
|
|
71
|
+
} else {
|
|
72
|
+
console.log(taproot.miniscript); // multi_a(2,A,B,C)
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## API
|
|
77
|
+
|
|
78
|
+
### `compilePolicy(policy: string)`
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
|
|
82
|
+
```ts
|
|
83
|
+
{
|
|
84
|
+
miniscript: string;
|
|
85
|
+
asm: string;
|
|
86
|
+
issane: boolean;
|
|
87
|
+
issanesublevel: boolean;
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Notes:
|
|
92
|
+
|
|
93
|
+
- This follows the reference compiler behavior from
|
|
94
|
+
[`sipa/miniscript`](https://github.com/sipa/miniscript).
|
|
95
|
+
- `asm`, `issane`, and `issanesublevel` are computed by the bundled C++
|
|
96
|
+
reference implementation and are exposed for informational/compatibility
|
|
97
|
+
purposes.
|
|
98
|
+
- On failure, `miniscript` and `asm` are `"[compile error]"`.
|
|
99
|
+
|
|
100
|
+
### `compilePolicyTaproot(policy: string)`
|
|
101
|
+
|
|
102
|
+
Returns:
|
|
103
|
+
|
|
104
|
+
```ts
|
|
105
|
+
{
|
|
106
|
+
miniscript: string;
|
|
107
|
+
issane: boolean;
|
|
108
|
+
}
|
|
19
109
|
```
|
|
20
110
|
|
|
111
|
+
Notes:
|
|
112
|
+
|
|
113
|
+
- Same result shape style as `compilePolicy`, but without `asm`.
|
|
114
|
+
- Recommended success check is `issane`.
|
|
115
|
+
- On failure, `issane` is `false` and `miniscript` is an error string:
|
|
116
|
+
|
|
117
|
+
```ts
|
|
118
|
+
{
|
|
119
|
+
miniscript:
|
|
120
|
+
| '[compile error]'
|
|
121
|
+
| `[exception: ...]`
|
|
122
|
+
| `[taproot rewrite error] ...`
|
|
123
|
+
| `[taproot rewrite exception] ...`;
|
|
124
|
+
issane: false;
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### `ready: Promise<void>`
|
|
129
|
+
|
|
130
|
+
Await this once before calling any compiler function.
|
|
131
|
+
|
|
132
|
+
## Related package
|
|
133
|
+
|
|
134
|
+
If you need Miniscript analysis, parsing, satisfier, or tapscript checks beyond
|
|
135
|
+
policy-to-miniscript compilation, use
|
|
136
|
+
[`@bitcoinerlab/miniscript`](https://github.com/bitcoinerlab/miniscript).
|
|
137
|
+
|
|
138
|
+
For application-level validation in both P2WSH and Taproot contexts, prefer
|
|
139
|
+
using `@bitcoinerlab/miniscript` directly. In this package, the reference
|
|
140
|
+
`asm`/sanity fields are returned as informational outputs.
|
|
141
|
+
|
|
142
|
+
## Upstream credit
|
|
143
|
+
|
|
144
|
+
This package is built on top of Pieter Wuille's upstream Miniscript work:
|
|
145
|
+
|
|
146
|
+
- https://github.com/sipa/miniscript
|
|
147
|
+
- https://bitcoin.sipa.be/miniscript/
|
|
148
|
+
|
|
21
149
|
## Build
|
|
22
150
|
|
|
23
|
-
This package uses Emscripten.
|
|
151
|
+
This package uses Emscripten.
|
|
24
152
|
|
|
25
153
|
```bash
|
|
26
154
|
make clean && make
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var miniscript = require('@bitcoinerlab/miniscript');
|
|
4
|
+
|
|
3
5
|
function getDefaultExportFromCjs (x) {
|
|
4
6
|
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
|
|
5
7
|
}
|
|
@@ -52,7 +54,7 @@ var bindings = /*@__PURE__*/getDefaultExportFromCjs(bindingsExports);
|
|
|
52
54
|
|
|
53
55
|
// Initial author: Pieter Wuille ( https://github.com/sipa/miniscript/blob/master/index.html)
|
|
54
56
|
// Adapted by Jose-Luis Landabaso - https://bitcoinerlab.com:
|
|
55
|
-
// compilePolicy
|
|
57
|
+
// compilePolicy and compilePolicyTaproot with sanity flags
|
|
56
58
|
|
|
57
59
|
|
|
58
60
|
const cleanAsm = asm =>
|
|
@@ -63,7 +65,6 @@ const cleanAsm = asm =>
|
|
|
63
65
|
|
|
64
66
|
let Module;
|
|
65
67
|
let em_miniscript_compile;
|
|
66
|
-
let em_miniscript_analyze;
|
|
67
68
|
let modulePromise;
|
|
68
69
|
|
|
69
70
|
const initModule = () => {
|
|
@@ -79,13 +80,6 @@ const initModule = () => {
|
|
|
79
80
|
'number',
|
|
80
81
|
'number'
|
|
81
82
|
]);
|
|
82
|
-
em_miniscript_analyze = Module.cwrap('miniscript_analyze', 'none', [
|
|
83
|
-
'string',
|
|
84
|
-
'number',
|
|
85
|
-
'number',
|
|
86
|
-
'number',
|
|
87
|
-
'number'
|
|
88
|
-
]);
|
|
89
83
|
return Module;
|
|
90
84
|
});
|
|
91
85
|
}
|
|
@@ -96,7 +90,7 @@ const initModule = () => {
|
|
|
96
90
|
const ready = initModule();
|
|
97
91
|
|
|
98
92
|
const ensureReady = () => {
|
|
99
|
-
if (!Module || !em_miniscript_compile
|
|
93
|
+
if (!Module || !em_miniscript_compile) {
|
|
100
94
|
throw new Error('Miniscript bindings not ready. Await ready before calling compile functions.');
|
|
101
95
|
}
|
|
102
96
|
};
|
|
@@ -110,13 +104,11 @@ const ensureReady = () => {
|
|
|
110
104
|
*/
|
|
111
105
|
|
|
112
106
|
/**
|
|
113
|
-
* @typedef {Object}
|
|
114
|
-
* @property {string}
|
|
107
|
+
* @typedef {Object} CompilePolicyTaprootResult
|
|
108
|
+
* @property {string} miniscript - Rewritten tapscript miniscript, or an error marker string.
|
|
115
109
|
* @property {boolean} issane - Whether the miniscript is sane at the top level.
|
|
116
|
-
* @property {boolean} issanesublevel - Whether the miniscript is sane at the sublevel.
|
|
117
110
|
*/
|
|
118
111
|
|
|
119
|
-
|
|
120
112
|
/**
|
|
121
113
|
* Compiles a miniscript policy into a miniscript expression (if possible).
|
|
122
114
|
* @function
|
|
@@ -160,45 +152,69 @@ const compilePolicy = policy => {
|
|
|
160
152
|
return result;
|
|
161
153
|
};
|
|
162
154
|
|
|
155
|
+
const isCompileFailure = miniscript =>
|
|
156
|
+
typeof miniscript !== 'string' ||
|
|
157
|
+
miniscript === '[compile error]' ||
|
|
158
|
+
miniscript.startsWith('[exception:');
|
|
159
|
+
|
|
160
|
+
const rewriteForTapscript = miniscript => miniscript.replace(/\bmulti\(/g, 'multi_a(');
|
|
161
|
+
const taprootRewriteError = message => `[taproot rewrite error] ${message}`;
|
|
162
|
+
const taprootRewriteException = message => `[taproot rewrite exception] ${message}`;
|
|
163
|
+
|
|
163
164
|
/**
|
|
164
|
-
* Compiles a miniscript
|
|
165
|
-
*
|
|
165
|
+
* Compiles a miniscript policy into a tapscript miniscript expression.
|
|
166
|
+
*
|
|
167
|
+
* Strict mode behavior:
|
|
168
|
+
* 1) compile with the C++ policy compiler
|
|
169
|
+
* 2) rewrite multi() to multi_a()
|
|
170
|
+
* 3) validate rewritten miniscript under tapscript rules
|
|
166
171
|
*
|
|
167
|
-
* @param {string}
|
|
168
|
-
* @returns {
|
|
172
|
+
* @param {string} policy - The miniscript policy to compile.
|
|
173
|
+
* @returns {CompilePolicyTaprootResult}
|
|
169
174
|
*/
|
|
170
|
-
const
|
|
175
|
+
const compilePolicyTaproot = policy => {
|
|
171
176
|
ensureReady();
|
|
172
|
-
const analysis = Module._malloc(50000);
|
|
173
|
-
const asm = Module._malloc(100000);
|
|
174
|
-
const issane = Module._malloc(10);
|
|
175
|
-
const issanesublevel = Module._malloc(10);
|
|
176
|
-
em_miniscript_analyze(
|
|
177
|
-
miniscript,
|
|
178
|
-
analysis,
|
|
179
|
-
50000,
|
|
180
|
-
asm,
|
|
181
|
-
100000,
|
|
182
|
-
issane,
|
|
183
|
-
10,
|
|
184
|
-
issanesublevel,
|
|
185
|
-
10
|
|
186
|
-
);
|
|
187
|
-
const result_asm = Module.UTF8ToString(asm);
|
|
188
|
-
const result_issane = Module.UTF8ToString(issane);
|
|
189
|
-
const result_issanesublebel = Module.UTF8ToString(issanesublevel);
|
|
190
|
-
Module._free(analysis);
|
|
191
|
-
Module._free(asm);
|
|
192
|
-
Module._free(issane);
|
|
193
|
-
Module._free(issanesublevel);
|
|
194
177
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
178
|
+
const base = compilePolicy(policy);
|
|
179
|
+
|
|
180
|
+
if (isCompileFailure(base.miniscript)) {
|
|
181
|
+
return {
|
|
182
|
+
miniscript:
|
|
183
|
+
typeof base.miniscript === 'string' ? base.miniscript : '[compile error]',
|
|
184
|
+
issane: false
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const miniscript$1 = rewriteForTapscript(base.miniscript);
|
|
189
|
+
|
|
190
|
+
try {
|
|
191
|
+
const analysis = miniscript.analyzeMiniscript(miniscript$1, { tapscript: true });
|
|
192
|
+
const isValidAndSane = analysis.valid && analysis.issane && analysis.issanesublevel;
|
|
193
|
+
|
|
194
|
+
if (!isValidAndSane) {
|
|
195
|
+
return {
|
|
196
|
+
miniscript: taprootRewriteError(
|
|
197
|
+
analysis.error ||
|
|
198
|
+
'multi() to multi_a() rewrite produced miniscript that is not valid/sane in tapscript context.'
|
|
199
|
+
),
|
|
200
|
+
issane: false
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return {
|
|
205
|
+
miniscript: miniscript$1,
|
|
206
|
+
issane: analysis.issane
|
|
207
|
+
};
|
|
208
|
+
} catch (error) {
|
|
209
|
+
return {
|
|
210
|
+
miniscript: taprootRewriteException(
|
|
211
|
+
error instanceof Error ? error.message : String(error)
|
|
212
|
+
),
|
|
213
|
+
issane: false
|
|
214
|
+
};
|
|
215
|
+
}
|
|
200
216
|
};
|
|
201
217
|
|
|
202
|
-
exports.compileMiniscript = compileMiniscript;
|
|
203
218
|
exports.compilePolicy = compilePolicy;
|
|
219
|
+
exports.compilePolicyTaproot = compilePolicyTaproot;
|
|
204
220
|
exports.ready = ready;
|
package/package.json
CHANGED
|
@@ -11,10 +11,14 @@
|
|
|
11
11
|
"descriptors"
|
|
12
12
|
],
|
|
13
13
|
"homepage": "https://bitcoinerlab.com/modules/miniscript",
|
|
14
|
-
"version": "1.
|
|
14
|
+
"version": "1.1.0",
|
|
15
15
|
"description": "Bitcoin Miniscript policy compiler",
|
|
16
16
|
"main": "dist/index.js",
|
|
17
17
|
"types": "types/index.d.ts",
|
|
18
|
+
"files": [
|
|
19
|
+
"dist",
|
|
20
|
+
"types"
|
|
21
|
+
],
|
|
18
22
|
"scripts": {
|
|
19
23
|
"build": "rollup -c --bundleConfigAsCjs",
|
|
20
24
|
"build:prod": "NODE_ENV=production rollup -c --bundleConfigAsCjs",
|
|
@@ -44,6 +48,7 @@
|
|
|
44
48
|
"rollup": "^3.29.5"
|
|
45
49
|
},
|
|
46
50
|
"dependencies": {
|
|
51
|
+
"@bitcoinerlab/miniscript": "^2.0.0",
|
|
47
52
|
"bip68": "^1.0.4"
|
|
48
53
|
},
|
|
49
54
|
"bugs": {
|
package/types/index.d.ts
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
|
-
export declare const compilePolicy: (
|
|
1
|
+
export declare const compilePolicy: (policy: string) => {
|
|
2
2
|
miniscript: string;
|
|
3
3
|
asm: string;
|
|
4
4
|
issane: boolean;
|
|
5
5
|
issanesublevel: boolean;
|
|
6
6
|
};
|
|
7
7
|
|
|
8
|
-
export
|
|
9
|
-
|
|
8
|
+
export type CompilePolicyTaprootResult = {
|
|
9
|
+
miniscript: string;
|
|
10
10
|
issane: boolean;
|
|
11
|
-
issanesublevel: boolean;
|
|
12
11
|
};
|
|
13
12
|
|
|
13
|
+
export declare const compilePolicyTaproot: (
|
|
14
|
+
policy: string
|
|
15
|
+
) => CompilePolicyTaprootResult;
|
|
16
|
+
|
|
14
17
|
export declare const ready: Promise<void>;
|
package/Makefile
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
MINISCRIPT_COMMIT ?= 6806dfb15a1fafabf7dd28aae3c9d2bc49db01f1
|
|
2
|
-
# Pinned miniscript commit: "Merge sipa/miniscript#133: Hash clarifications"
|
|
3
|
-
HEADERS := miniscript/bitcoin/util/vector.h miniscript/bitcoin/util/strencodings.h miniscript/bitcoin/span.h miniscript/bitcoin/util/spanparsing.h miniscript/bitcoin/script/script.h miniscript/bitcoin/script/miniscript.h miniscript/compiler.h miniscript/bitcoin/crypto/common.h miniscript/bitcoin/serialize.h miniscript/bitcoin/prevector.h miniscript/bitcoin/compat/endian.h miniscript/bitcoin/compat/byteswap.h miniscript/bitcoin/attributes.h miniscript/bitcoin/tinyformat.h miniscript/bitcoin/primitives/transaction.h
|
|
4
|
-
SOURCES := miniscript/bitcoin/util/strencodings.cpp miniscript/bitcoin/util/spanparsing.cpp miniscript/bitcoin/script/script.cpp miniscript/bitcoin/script/miniscript.cpp miniscript/compiler.cpp
|
|
5
|
-
src/bindings.js: miniscript $(HEADERS) $(SOURCES) miniscript/js_bindings.cpp
|
|
6
|
-
em++ -O3 -g0 -Wall -std=c++17 -fno-rtti -flto -Iminiscript/bitcoin $(SOURCES) miniscript/js_bindings.cpp -s WASM=0 -s EXPORT_ES6=0 --memory-init-file 0 -s MODULARIZE=1 -s MALLOC=emmalloc -s WASM_ASYNC_COMPILATION=0 -s FILESYSTEM=0 -s ENVIRONMENT=web -s DISABLE_EXCEPTION_CATCHING=0 -s EXPORTED_FUNCTIONS='["_miniscript_compile","_miniscript_analyze","_malloc","_free"]' -s EXPORTED_RUNTIME_METHODS='["cwrap","UTF8ToString"]' -o src/bindings.js
|
|
7
|
-
miniscript:
|
|
8
|
-
git clone https://github.com/sipa/miniscript
|
|
9
|
-
git -C miniscript reset --hard $(MINISCRIPT_COMMIT)
|
|
10
|
-
#Patch: https://github.com/sipa/miniscript/pull/132
|
|
11
|
-
patch -p0 miniscript/compiler.cpp < patches/uppercase-h.patch
|
|
12
|
-
cp js_bindings.cpp miniscript/
|
|
13
|
-
clean:
|
|
14
|
-
rm -rf miniscript src/bindings.js
|
package/example.js
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
// To run it: "node ./example.js"
|
|
2
|
-
|
|
3
|
-
const { compilePolicy, compileMiniscript, ready } = require("./dist/index.js");
|
|
4
|
-
|
|
5
|
-
(async () => {
|
|
6
|
-
await ready;
|
|
7
|
-
|
|
8
|
-
const policy = "or(and(pk(A),older(8640)),pk(B))";
|
|
9
|
-
|
|
10
|
-
const {
|
|
11
|
-
miniscript,
|
|
12
|
-
asm: asmFromPolicy,
|
|
13
|
-
issane: issaneFromPolicy,
|
|
14
|
-
} = compilePolicy(policy);
|
|
15
|
-
|
|
16
|
-
const { asm: asmFromMiniscript, issane: issaneFromMiniscript } =
|
|
17
|
-
compileMiniscript(miniscript);
|
|
18
|
-
|
|
19
|
-
console.assert(asmFromPolicy === asmFromMiniscript, "ERROR: Asm mismatch.");
|
|
20
|
-
console.assert(
|
|
21
|
-
issaneFromPolicy === issaneFromMiniscript,
|
|
22
|
-
"ERROR: issane mismatch.",
|
|
23
|
-
);
|
|
24
|
-
|
|
25
|
-
console.log({
|
|
26
|
-
miniscript,
|
|
27
|
-
asm: asmFromMiniscript,
|
|
28
|
-
issane: issaneFromMiniscript,
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
console.log(
|
|
32
|
-
compileMiniscript("and_v(v:pk(key),or_b(l:after(100),al:after(200)))"),
|
|
33
|
-
);
|
|
34
|
-
})();
|
package/js_bindings.cpp
DELETED
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
// Initial author: Pieter Wuille
|
|
2
|
-
// Adapted by Jose-Luis Landabaso so that miniscript_compile and miniscript_analyze
|
|
3
|
-
// return issane & issanesublevel
|
|
4
|
-
#include <string>
|
|
5
|
-
|
|
6
|
-
#include <script/miniscript.h>
|
|
7
|
-
|
|
8
|
-
#include "compiler.h"
|
|
9
|
-
|
|
10
|
-
namespace {
|
|
11
|
-
|
|
12
|
-
using miniscript::operator"" _mst;
|
|
13
|
-
|
|
14
|
-
void Output(const std::string& str, char* out, int outlen) {
|
|
15
|
-
int maxlen = std::min<int>(outlen - 1, str.size());
|
|
16
|
-
memcpy(out, str.c_str(), maxlen);
|
|
17
|
-
out[maxlen] = 0;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
std::string Props(const miniscript::NodeRef<std::string>& node, std::string in) {
|
|
21
|
-
std::string ret = "<span title=\"type: ";
|
|
22
|
-
if (node->GetType() == ""_mst) {
|
|
23
|
-
ret += "[invalid]";
|
|
24
|
-
} else {
|
|
25
|
-
if (node->GetType() << "B"_mst) ret += 'B';
|
|
26
|
-
if (node->GetType() << "V"_mst) ret += 'V';
|
|
27
|
-
if (node->GetType() << "W"_mst) ret += 'W';
|
|
28
|
-
if (node->GetType() << "K"_mst) ret += 'K';
|
|
29
|
-
if (node->GetType() << "z"_mst) ret += 'z';
|
|
30
|
-
if (node->GetType() << "o"_mst) ret += 'o';
|
|
31
|
-
if (node->GetType() << "n"_mst) ret += 'n';
|
|
32
|
-
if (node->GetType() << "d"_mst) ret += 'd';
|
|
33
|
-
if (node->GetType() << "f"_mst) ret += 'f';
|
|
34
|
-
if (node->GetType() << "e"_mst) ret += 'e';
|
|
35
|
-
if (node->GetType() << "m"_mst) ret += 'm';
|
|
36
|
-
if (node->GetType() << "u"_mst) ret += 'u';
|
|
37
|
-
if (node->GetType() << "s"_mst) ret += 's';
|
|
38
|
-
if (node->GetType() << "k"_mst) ret += 'k';
|
|
39
|
-
}
|
|
40
|
-
ret += " scriptlen: " + std::to_string(node->ScriptSize());
|
|
41
|
-
ret += " max ops: " + std::to_string(node->GetOps());
|
|
42
|
-
ret += " max stack size: " + std::to_string(node->GetStackSize());
|
|
43
|
-
return std::move(ret) + "\">" + std::move(in) + "</span>";
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
std::string Analyze(const miniscript::NodeRef<std::string>& node) {
|
|
47
|
-
switch (node->fragment) {
|
|
48
|
-
case miniscript::Fragment::PK_K: {
|
|
49
|
-
return Props(node, "pk_k(" + (*COMPILER_CTX.ToString(node->keys[0])) + ")");
|
|
50
|
-
}
|
|
51
|
-
case miniscript::Fragment::PK_H: {
|
|
52
|
-
return Props(node, "pk_h(" + (*COMPILER_CTX.ToString(node->keys[0])) + ")");
|
|
53
|
-
}
|
|
54
|
-
case miniscript::Fragment::MULTI: return Props(node, "multi(" + std::to_string(node->k) + " of " + std::to_string(node->keys.size()) + ")");
|
|
55
|
-
case miniscript::Fragment::AFTER: return Props(node, "after(" + std::to_string(node->k) + ")");
|
|
56
|
-
case miniscript::Fragment::OLDER: return Props(node, "older(" + std::to_string(node->k) + ")");
|
|
57
|
-
case miniscript::Fragment::SHA256: return Props(node, "sha256()");
|
|
58
|
-
case miniscript::Fragment::RIPEMD160: return Props(node, "ripemd160()");
|
|
59
|
-
case miniscript::Fragment::HASH256: return Props(node, "hash256()");
|
|
60
|
-
case miniscript::Fragment::HASH160: return Props(node, "hash160()");
|
|
61
|
-
case miniscript::Fragment::JUST_0: return Props(node, "false");
|
|
62
|
-
case miniscript::Fragment::JUST_1: return Props(node, "true");
|
|
63
|
-
case miniscript::Fragment::WRAP_A: return Props(node, "a:") + " " + Analyze(node->subs[0]);
|
|
64
|
-
case miniscript::Fragment::WRAP_S: return Props(node, "s:") + " " + Analyze(node->subs[0]);
|
|
65
|
-
case miniscript::Fragment::WRAP_C: return Props(node, "c:") + " " + Analyze(node->subs[0]);
|
|
66
|
-
case miniscript::Fragment::WRAP_D: return Props(node, "d:") + " " + Analyze(node->subs[0]);
|
|
67
|
-
case miniscript::Fragment::WRAP_V: return Props(node, "v:") + " " + Analyze(node->subs[0]);
|
|
68
|
-
case miniscript::Fragment::WRAP_N: return Props(node, "n:") + " " + Analyze(node->subs[0]);
|
|
69
|
-
case miniscript::Fragment::WRAP_J: return Props(node, "j:") + " " + Analyze(node->subs[0]);
|
|
70
|
-
case miniscript::Fragment::AND_V: return Props(node, "and_v") + "<ul style=\"list-style-type: disc;\"><li>" + Analyze(node->subs[0]) + "</li><li>" + Analyze(node->subs[1]) + "</li></ul>";
|
|
71
|
-
case miniscript::Fragment::AND_B: return Props(node, "and_b") + "<ul style=\"list-style-type: disc;\"><li>" + Analyze(node->subs[0]) + "</li><li>" + Analyze(node->subs[1]) + "</li></ul>";
|
|
72
|
-
case miniscript::Fragment::OR_B: return Props(node, "or_b") + "<ul style=\"list-style-type: disc;\"><li>" + Analyze(node->subs[0]) + "</li><li>" + Analyze(node->subs[1]) + "</li></ul>";
|
|
73
|
-
case miniscript::Fragment::OR_C: return Props(node, "or_c") + "<ul style=\"list-style-type: disc;\"><li>" + Analyze(node->subs[0]) + "</li><li>" + Analyze(node->subs[1]) + "</li></ul>";
|
|
74
|
-
case miniscript::Fragment::OR_D: return Props(node, "or_d") + "<ul style=\"list-style-type: disc;\"><li>" + Analyze(node->subs[0]) + "</li><li>" + Analyze(node->subs[1]) + "</li></ul>";
|
|
75
|
-
case miniscript::Fragment::OR_I: return Props(node, "or_i") + "<ul style=\"list-style-type: disc;\"><li>" + Analyze(node->subs[0]) + "</li><li>" + Analyze(node->subs[1]) + "</li></ul>";
|
|
76
|
-
case miniscript::Fragment::ANDOR: return Props(node, "andor [or]") + "<ul style=\"list-style-type: disc;\"><li>andor [and]<ul style=\"list-style-type: disc;\"><li>" + Analyze(node->subs[0]) + "</li><li>" + Analyze(node->subs[1]) + "</li></ul></li><li>" + Analyze(node->subs[2]) + "</li></ul>";
|
|
77
|
-
case miniscript::Fragment::THRESH: {
|
|
78
|
-
auto ret = Props(node, "thresh(" + std::to_string(node->k) + " of " + std::to_string(node->subs.size()) + ")") + "<ul style=\"list-style-type: disc;\">";
|
|
79
|
-
for (const auto& sub : node->subs) {
|
|
80
|
-
ret += "<li>" + Analyze(sub) + "</li>";
|
|
81
|
-
}
|
|
82
|
-
return std::move(ret) + "</ul>";
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
extern "C" {
|
|
90
|
-
|
|
91
|
-
void miniscript_compile(const char* desc, char* msout, int msoutlen, char* costout, int costoutlen, char* asmout, int asmoutlen, char* issane, int issanelen, char* issanesublevel, int issanesublevellen) {
|
|
92
|
-
try {
|
|
93
|
-
std::string str(desc);
|
|
94
|
-
str.erase(str.find_last_not_of(" \n\r\t") + 1);
|
|
95
|
-
miniscript::NodeRef<std::string> ret;
|
|
96
|
-
double avgcost;
|
|
97
|
-
if (!Compile(Expand(str), ret, avgcost)) {
|
|
98
|
-
Output("[compile error]", msout, msoutlen);
|
|
99
|
-
Output("[compile error]", costout, costoutlen);
|
|
100
|
-
Output("[compile error]", asmout, asmoutlen);
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
Output(Abbreviate(*(ret->ToString(COMPILER_CTX))), msout, msoutlen);
|
|
104
|
-
std::string coststr = "<ul><li>Script: " + std::to_string(ret->ScriptSize()) + " WU</li><li>Input: " + std::to_string(avgcost) + " WU</li><li>Total: " + std::to_string(ret->ScriptSize() + avgcost) + " WU</li></ul>";
|
|
105
|
-
Output(coststr, costout, costoutlen);
|
|
106
|
-
Output(Disassemble(ret->ToScript(COMPILER_CTX)), asmout, asmoutlen);
|
|
107
|
-
if (ret->IsSane()) {
|
|
108
|
-
Output("true", issanesublevel, issanesublevellen);
|
|
109
|
-
} else {
|
|
110
|
-
Output("false", issanesublevel, issanesublevellen);
|
|
111
|
-
}
|
|
112
|
-
if (ret->IsValidTopLevel()) {
|
|
113
|
-
Output("true", issane, issanelen);
|
|
114
|
-
} else {
|
|
115
|
-
Output("false", issane, issanelen);
|
|
116
|
-
}
|
|
117
|
-
} catch (const std::exception& e) {
|
|
118
|
-
Output("[exception: " + std::string(e.what()) + "]", msout, msoutlen);
|
|
119
|
-
Output("", costout, costoutlen);
|
|
120
|
-
Output("", asmout, asmoutlen);
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
void miniscript_analyze(const char* ms, char* costout, int costoutlen, char* asmout, int asmoutlen, char* issane, int issanelen, char* issanesublevel, int issanesublevellen) {
|
|
125
|
-
try {
|
|
126
|
-
std::string str(ms);
|
|
127
|
-
str.erase(str.find_last_not_of(" \n\r\t") + 1);
|
|
128
|
-
miniscript::NodeRef<std::string> ret;
|
|
129
|
-
ret = miniscript::FromString(Expand(str), COMPILER_CTX);
|
|
130
|
-
if (!ret || !ret->IsValidTopLevel()) {
|
|
131
|
-
Output("[analysis error]", costout, costoutlen);
|
|
132
|
-
Output("[analysis error]", asmout, asmoutlen);
|
|
133
|
-
return;
|
|
134
|
-
}
|
|
135
|
-
std::string coststr = "Size: " + std::to_string(ret->ScriptSize()) + " bytes script<ul><li>" + Analyze(ret) + "</li></ul>";
|
|
136
|
-
Output(coststr, costout, costoutlen);
|
|
137
|
-
Output(Disassemble(ret->ToScript(COMPILER_CTX)), asmout, asmoutlen);
|
|
138
|
-
if (ret->IsSane()) {
|
|
139
|
-
Output("true", issanesublevel, issanesublevellen);
|
|
140
|
-
} else {
|
|
141
|
-
Output("false", issanesublevel, issanesublevellen);
|
|
142
|
-
}
|
|
143
|
-
if (ret->IsValidTopLevel()) {
|
|
144
|
-
Output("true", issane, issanelen);
|
|
145
|
-
} else {
|
|
146
|
-
Output("false", issane, issanelen);
|
|
147
|
-
}
|
|
148
|
-
} catch (const std::exception& e) {
|
|
149
|
-
Output("[exception: " + std::string(e.what()) + "]", costout, costoutlen);
|
|
150
|
-
Output("", asmout, asmoutlen);
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
--- compiler.cpp.bak 2026-01-30 13:12:43
|
|
2
|
-
+++ compiler.cpp 2026-01-30 13:12:54
|
|
3
|
-
@@ -848,7 +848,7 @@
|
|
4
|
-
}
|
|
5
|
-
if (data.size() == 20) {
|
|
6
|
-
if (data == std::vector<unsigned char>(20, 0x99)) {
|
|
7
|
-
- ret += "<h>";
|
|
8
|
-
+ ret += "<H>";
|
|
9
|
-
} else if (data[0] == 'P' && data[1] == 'K' && data[2] == 'h') {
|
|
10
|
-
while (data.size() && data.back() == 0) data.pop_back();
|
|
11
|
-
ret += "<HASH160(" + std::string((const char*)data.data() + 3, data.size() - 3) + ")>";
|
package/rollup.config.js
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
import commonjs from "@rollup/plugin-commonjs";
|
|
3
|
-
|
|
4
|
-
export default {
|
|
5
|
-
input: "./src/index.js",
|
|
6
|
-
output: {
|
|
7
|
-
file: "./dist/index.js",
|
|
8
|
-
format: "cjs",
|
|
9
|
-
},
|
|
10
|
-
plugins: [commonjs()],
|
|
11
|
-
// Tell Rollup not to bundle dependencies listed in package.json
|
|
12
|
-
external: [...Object.keys(require("./package.json").dependencies || {})],
|
|
13
|
-
};
|