Skip to content

Commit f6d0330

Browse files
feat: add op_drop to wasm-miniscript
Issue: BTC-1451
1 parent a4018fe commit f6d0330

File tree

6 files changed

+160
-33
lines changed

6 files changed

+160
-33
lines changed

packages/wasm-miniscript/Cargo.lock

Lines changed: 50 additions & 32 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/wasm-miniscript/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,4 @@ crate-type = ["cdylib"]
99
[dependencies]
1010
wasm-bindgen = "0.2"
1111
js-sys = "0.3"
12-
miniscript = { version = "12.2.0" }
12+
miniscript = { git = "https://github.com/BitGo/rust-miniscript", branch = "opdrop" }

packages/wasm-miniscript/src/try_into_js_value.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ impl<Pk: MiniscriptKey + TryIntoJsValue, Ctx: ScriptContext> TryIntoJsValue for
158158
Terminal::Check(node) => js_obj!("Check" => node),
159159
Terminal::DupIf(node) => js_obj!("DupIf" => node),
160160
Terminal::Verify(node) => js_obj!("Verify" => node),
161+
Terminal::Drop(node) => js_obj!("Drop" => node),
161162
Terminal::NonZero(node) => js_obj!("NonZero" => node),
162163
Terminal::ZeroNotEqual(node) => js_obj!("ZeroNotEqual" => node),
163164
Terminal::AndV(a, b) => js_obj!("AndV" => js_arr!(a, b)),
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import * as fs from "fs/promises";
2+
export async function getFixture(path: string, defaultValue: unknown): Promise<unknown> {
3+
try {
4+
return JSON.parse(await fs.readFile(path, "utf8"));
5+
} catch (e) {
6+
if (e.code === "ENOENT") {
7+
await fs.writeFile(path, JSON.stringify(defaultValue, null, 2));
8+
throw new Error(`Fixture not found at ${path}, created a new one`);
9+
}
10+
}
11+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
"0200000000010100000000000000000000000000000000000000000000000000000000000000000000000000feffffff0100e1f505000000002200206e3041069586d8bd9aec6ab1ac95f17d612c45cc1a76a4791aedab1c28a2109e040047304402207e7faabc574e1d4b482c1e3415fe7f1a9eb1d6a6d19982b6c1e5f2f2f9bb51eb02205716944bae604e3a25d450a9a413133b246ffe0fb17038dab217167060294f6e01473044022052ae4cf5f4093b655a4a86c5233832dd1907c7496123e806630ef3d30c60f00e02205a44f2c8fffc49358021787b123a4ae1d0b97735f6cf4769809fb2d214c1b657016e020004b175522102ae7c3c0ebc315a33151a1985ebb1fdcae72b3b91c38e3193c40ebabfffe9c343210260ba2407f7c75d525db9f171e9b2f3cf5ba3f0d7fc6067b20d4b91585432f9742103eadd6e4300dac62f1d4cf1131a06c5e140911f04245c64934c27510e93dbe84353ae00040000"
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import * as assert from "assert";
2+
import * as utxolib from "@bitgo/utxo-lib";
3+
import { Descriptor } from "../js";
4+
import { finalizePsbt, updateInputWithDescriptor } from "./psbt.util";
5+
import { getFixture } from "./fixtures";
6+
7+
const rootWalletKeys = new utxolib.bitgo.RootWalletKeys(utxolib.testutil.getKeyTriple("wasm"));
8+
9+
function getDescriptorOpDropP2ms(locktime: number, keys: utxolib.BIP32Interface[]) {
10+
const xpubs = keys.map((key) => key.toBase58() + "/*");
11+
// the `r:` prefix is a custom BitGo modification of miniscript to allow OP_DROP
12+
return `wsh(and_v(r:after(${locktime}),multi(2,${xpubs.join(",")})))`;
13+
}
14+
15+
describe("CLV with OP_DROP", () => {
16+
const locktime = 1024;
17+
const descriptor = Descriptor.fromString(
18+
getDescriptorOpDropP2ms(locktime, rootWalletKeys.triple),
19+
"derivable",
20+
);
21+
it("has expected AST", () => {
22+
assert.deepStrictEqual(descriptor.node(), {
23+
Wsh: {
24+
Ms: {
25+
AndV: [
26+
{
27+
Drop: {
28+
After: {
29+
absLockTime: 1024,
30+
},
31+
},
32+
},
33+
{
34+
Multi: [
35+
2,
36+
{
37+
XPub: "xpub661MyMwAqRbcFNusVUbSN3nbanHMtJjLgZGrs1wxH6f77kKQd6Vq4HfkZQNPC1vSbN6RTiBWJJV6FwJtCfBon2SgaT2J3MSkydukstKjwbJ/*",
38+
},
39+
{
40+
XPub: "xpub661MyMwAqRbcFo3t7PUqvbgvAcEuuoeVib5aapsg52inrG6KGF5aNtR5ey1FNCt1zJpMQiNec5XpofQmLNRhHvQRbhkc8UsWwwMwsXW6ogU/*",
41+
},
42+
{
43+
XPub: "xpub661MyMwAqRbcGg7f22Kcg2gy1F4jBjWR3xQTECVeJPHmxvhg5gUAZC6EYFtnyi6aMDQir1kV8HzCqC2FzTowGgEZqRh7rinqUCDeNDdmYzH/*",
44+
},
45+
],
46+
},
47+
],
48+
},
49+
},
50+
});
51+
});
52+
53+
it("has expected asm", () => {
54+
assert.deepStrictEqual(descriptor.atDerivationIndex(0).toAsmString().split(" "), [
55+
"OP_PUSHBYTES_2",
56+
"0004",
57+
"OP_CLTV",
58+
"OP_DROP",
59+
"OP_PUSHNUM_2",
60+
"OP_PUSHBYTES_33",
61+
"02ae7c3c0ebc315a33151a1985ebb1fdcae72b3b91c38e3193c40ebabfffe9c343",
62+
"OP_PUSHBYTES_33",
63+
"0260ba2407f7c75d525db9f171e9b2f3cf5ba3f0d7fc6067b20d4b91585432f974",
64+
"OP_PUSHBYTES_33",
65+
"03eadd6e4300dac62f1d4cf1131a06c5e140911f04245c64934c27510e93dbe843",
66+
"OP_PUSHNUM_3",
67+
"OP_CHECKMULTISIG",
68+
]);
69+
});
70+
71+
it("can be signed", async function () {
72+
const psbt = Object.assign(new utxolib.Psbt({ network: utxolib.networks.bitcoin }), {
73+
locktime,
74+
});
75+
const signers = rootWalletKeys.triple.slice(0, 2);
76+
const descriptorAt0 = descriptor.atDerivationIndex(0);
77+
const script = Buffer.from(descriptorAt0.scriptPubkey());
78+
psbt.addInput({
79+
hash: Buffer.alloc(32),
80+
index: 0,
81+
sequence: 0xfffffffe,
82+
witnessUtxo: { script, value: BigInt(1e8) },
83+
});
84+
psbt.addOutput({ script, value: BigInt(1e8) });
85+
updateInputWithDescriptor(psbt, 0, descriptorAt0);
86+
for (const signer of signers) {
87+
psbt.signAllInputsHD(signer);
88+
}
89+
finalizePsbt(psbt);
90+
const signedTx = psbt.extractTransaction().toBuffer();
91+
assert.strictEqual(
92+
signedTx.toString("hex"),
93+
await getFixture("test/fixtures/opdrop.json", signedTx.toString("hex")),
94+
);
95+
});
96+
});

0 commit comments

Comments
 (0)