Skip to content

Commit 752571e

Browse files
committed
Fixed parsing bug, began adding 'fromScript' support, and added test.
Fixed a bug in the AFTER fragment that used a token's 'parse' method instead of the parseContext's 'parse'. Also added the 'fromScript' method to a few fragments to allow conversions from ASM to miniscript strings. Jest was added as a test runner and a single 'fromScript' test has been added.
1 parent 45032e0 commit 752571e

File tree

13 files changed

+4041
-14
lines changed

13 files changed

+4041
-14
lines changed

examples/quick-start.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
import Miniscript from "..";
22

3-
let rootExpression = Miniscript.parse("thresh(1,pk(0xA),s:pk(0xB))");
3+
let rootExpression = Miniscript.parse("and_v(v:pk(key),or_b(l:after(100),al:after(200)))");
44
console.log(rootExpression.toScript());
5+
6+
// console.log(Miniscript.parseScript("7 OP_CHECKLOCKTIMEVERIFY 4 OP_CHECKLOCKTIMEVERIFY 2 OP_EQUAL"));
7+

index.ts

+8
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,12 @@ export default class Miniscript {
2828
let parser = new Parser(expressions, wrappers);
2929
return parser.parse(tokens);
3030
}
31+
32+
static parseScript(
33+
input: string,
34+
): any {
35+
let parser = new Parser(expressions, wrappers);
36+
let reversedScript = input.split(" ").reverse();
37+
return parser.parseScript(reversedScript);
38+
}
3139
}

jest.config.js

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
/** @type {import('ts-jest').JestConfigWithTsJest} */
2+
module.exports = {
3+
preset: 'ts-jest',
4+
testEnvironment: 'node',
5+
};

modules/bitcoin-miniscript/fragments/AFTER.ts

+15-7
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { NUMBER } from "../../../universal-tokens";
1+
import { CLOSE_PAREN, NUMBER } from "../../../universal-tokens";
22
import { lexKeyword } from "../../../lex-utils";
33
import { Types } from "../../../miniscript-types";
44
import {
@@ -37,10 +37,12 @@ export class AFTER
3737
static parse = (parseContext: ParseContext) => {
3838
parseContext.eat(this.tokenType);
3939

40-
let locktime = NUMBER.parse(parseContext);
40+
let locktime = parseContext.eat(NUMBER.tokenType)?.value;
4141

4242
this.validateLocktime(locktime);
4343

44+
parseContext.eat(CLOSE_PAREN.tokenType);
45+
4446
return new AFTER(locktime);
4547
};
4648

@@ -77,15 +79,21 @@ export class AFTER
7779
return `${this.locktime} OP_CHECKLOCKTIMEVERIFY`;
7880
};
7981

80-
static fromScript = (opcodes: string[]) => {
81-
let locktime = parseInt(opcodes[1]);
82-
if (isNaN(locktime)) {
82+
static fromScript = (scriptParseContext: any) => {
83+
let {reversedScript} = scriptParseContext;
84+
if (reversedScript[0] != "OP_CHECKLOCKTIMEVERIFY") {
8385
return;
8486
}
8587

86-
if (opcodes[0] == "OP_CHECKLOCKTIMEVERIFY") {
87-
return new AFTER(locktime);
88+
let locktime = parseInt(reversedScript[1]);
89+
if (isNaN(locktime)) {
90+
return;
8891
}
92+
93+
reversedScript.shift();
94+
reversedScript.shift();
95+
96+
return new AFTER(locktime);
8997
};
9098

9199
private static validateLocktime(locktime: number) {

modules/bitcoin-miniscript/fragments/ANDOR.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ export class ANDOR
171171
`\n${indent(this.children[2].toScript())}\n` +
172172
`OP_ELSE` +
173173
`\n${indent(this.children[1].toScript())}\n` +
174-
`OP_ENDIF`
174+
`OP_ENDIF\n`
175175
);
176176
};
177177
}

modules/bitcoin-miniscript/fragments/OR_I.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -119,8 +119,8 @@ export class OR_I
119119
`OP_IF` +
120120
`\n${indent(this.children[0].toScript())}\n` +
121121
`OP_ELSE` +
122-
`\n${this.children[1].toScript()}\n` +
123-
`OP_ENDIF`
122+
`\n${indent(this.children[1].toScript())}\n` +
123+
`OP_ENDIF\n`
124124
);
125125
};
126126
}

modules/bitcoin-miniscript/fragments/SHA256.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { lexKeyword } from "../../../lex-utils";
22
import { Types } from "../../../miniscript-types";
3-
import { eat } from "../../../parse-utils";
43
import { ParseContext } from "../../../parser";
54
import {
65
MiniscriptFragmentStatic,
@@ -55,7 +54,9 @@ export class SHA256
5554
parseContext.eat(CLOSE_PAREN.tokenType);
5655

5756
if (!stringToken?.value) {
58-
throw new Error(`String not found while parsing string in ${SHA256.tokenType}`);
57+
throw new Error(
58+
`String not found while parsing string in ${SHA256.tokenType}`
59+
);
5960
}
6061

6162
return new SHA256(stringToken.value as string);

modules/bitcoin-miniscript/fragments/THRESH.ts

+33
Original file line numberDiff line numberDiff line change
@@ -202,4 +202,37 @@ export class THRESH
202202
script + " " + this.k + " " + (verify ? `OP_EQUALVERIFY` : `OP_EQUAL`)
203203
);
204204
};
205+
206+
static fromScript = (scriptParseContext: any) => {
207+
let { reversedScript } = scriptParseContext;
208+
if (reversedScript[0] != "OP_EQUAL") {
209+
return;
210+
}
211+
let threshold = parseInt(reversedScript[1]);
212+
if (isNaN(threshold)) {
213+
return;
214+
}
215+
216+
reversedScript.shift();
217+
reversedScript.shift();
218+
219+
let i = threshold;
220+
let x;
221+
let children: any[] = [];
222+
do {
223+
if (i + 1 < threshold) {
224+
let OP_ADD = reversedScript.shift();
225+
if (OP_ADD != "OP_ADD") {
226+
throw new Error();
227+
}
228+
}
229+
230+
x = scriptParseContext.parser.parseScript(reversedScript);
231+
if(x) {
232+
children.push(x);
233+
}
234+
} while (x);
235+
236+
return new THRESH(children, threshold);
237+
};
205238
}

modules/bitcoin-miniscript/fragments/WRAP_A.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,6 @@ export class WRAP_A
8686
};
8787

8888
toScript = () => {
89-
return `TOALTSTACK ${this.children[0].toScript()} FROMALTSTACK`;
89+
return `OP_TOALTSTACK ${this.children[0].toScript()} OP_FROMALTSTACK`;
9090
};
9191
}

0 commit comments

Comments
 (0)