├── .gitignore ├── .npmignore ├── Cargo.toml ├── LICENSE ├── README.md ├── lib ├── index.js ├── methods │ ├── miniscript.js │ ├── script.js │ └── semantic.js ├── miniscript.js └── utils │ └── common.js ├── package.json └── src └── lib.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | /pkg 5 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "miniscript-js" 3 | version = "0.1.0" 4 | authors = ["nijynot "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [lib] 10 | crate-type = ["cdylib"] 11 | 12 | [dependencies] 13 | wasm-bindgen = { version = "0.2.50", features = ["serde-serialize"] } 14 | secp256k1 = "0.15.5" 15 | miniscript = { version = "0.10.0", features = ["compiler"] } 16 | bitcoin = "0.20.0" 17 | js-sys = "0.3.27" 18 | serde = "^1.0.59" 19 | serde_derive = "^1.0.59" 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) (https://github.com/nijynot) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Miniscript JS 2 | 3 | 4 | ## Install 5 | ```bash 6 | $ npm install --save miniscript-js 7 | ``` 8 | 9 | ## Usage 10 | ```js 11 | const miniscript = require('miniscript-js'); 12 | 13 | const ms = miniscript.parsePolicy('or(pk(A),pk(B))', 'STRING'); 14 | console.log(ms.semantic.minimumNKeys()); 15 | // => 2 16 | 17 | console.log(ms.script.encode()); 18 | // => OP_PUSHBYTES_33 022222222222222222222222222222222222222222222222222222222222222222 OP_CHECKSIGVERIFY OP_PUSHBYTES_33 022222222222222222222222222222222222222222222222222222222222222222 OP_CHECKSIG 19 | ``` 20 | 21 | ## License 22 | MIT 23 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | const pkg = require('../pkg/miniscript_js'); 2 | const Miniscript = require('./miniscript'); 3 | const common = require('./utils/common'); 4 | 5 | const { ScriptType } = common; 6 | 7 | function parsePolicy(script, pkType) { 8 | const ms = pkg.policy_to_miniscript(script); 9 | return new Miniscript({ 10 | miniscript: ms, 11 | scriptType: ScriptType.Miniscript, 12 | pkType, 13 | }); 14 | } 15 | 16 | function parseMiniscript(script, pkType) { 17 | return new Miniscript({ 18 | miniscript: script, 19 | scriptType: ScriptType.Miniscript, 20 | pkType, 21 | }); 22 | } 23 | 24 | module.exports = { 25 | parsePolicy, 26 | parseMiniscript, 27 | ...common, 28 | }; 29 | -------------------------------------------------------------------------------- /lib/methods/miniscript.js: -------------------------------------------------------------------------------- 1 | const pkg = require('../../pkg/miniscript_js'); 2 | 3 | /** 4 | * Get script size of a Miniscript. 5 | * @param {Object} options 6 | * @param {string} options.miniscript 7 | * @param {string} options.scriptType 8 | * @param {string} options.pkType 9 | */ 10 | function scriptSize(options) { 11 | const { miniscript, scriptType, pkType } = options; 12 | return pkg.script_size(miniscript, scriptType, pkType); 13 | } 14 | 15 | /** 16 | * Get script size of a Miniscript. 17 | * @param {Object} options 18 | * @param {string} options.miniscript 19 | * @param {string} options.scriptType 20 | * @param {string} options.pkType 21 | */ 22 | function maxSatisfactionWitnessElements(options) { 23 | const { miniscript, scriptType, pkType } = options; 24 | return pkg.max_satisfaction_witness_elements(miniscript, scriptType, pkType); 25 | } 26 | 27 | /** 28 | * Get script size of a Miniscript. 29 | * @param {Object} options 30 | * @param {string} options.miniscript 31 | * @param {string} options.scriptType 32 | * @param {string} options.pkType 33 | */ 34 | function maxSatisfactionSize(options) { 35 | const { miniscript, scriptType, pkType } = options; 36 | return pkg.max_satisfaction_size(miniscript, scriptType, pkType); 37 | } 38 | 39 | module.exports = { 40 | scriptSize, 41 | maxSatisfactionWitnessElements, 42 | maxSatisfactionSize, 43 | }; 44 | -------------------------------------------------------------------------------- /lib/methods/script.js: -------------------------------------------------------------------------------- 1 | const pkg = require('../../pkg/miniscript_js'); 2 | 3 | /** 4 | * Encode into a Bitcoin Script. 5 | * @param {Object} options 6 | * @param {string} options.miniscript 7 | * @param {string} options.scriptType 8 | * @param {string} options.pkType 9 | */ 10 | function encode(options) { 11 | const { miniscript, scriptType, pkType } = options; 12 | return pkg.to_script(miniscript, scriptType, pkType); 13 | } 14 | 15 | /** 16 | * Encode into a Bitcoin Script. 17 | * @param {Object} options 18 | * @param {string} options.miniscript 19 | * @param {string} options.scriptType 20 | * @param {string} options.pkType 21 | */ 22 | function len(options) { 23 | const { miniscript, scriptType, pkType } = options; 24 | return pkg.len(miniscript, scriptType, pkType); 25 | } 26 | 27 | /** 28 | * Encode into a Bitcoin Script. 29 | * @param {Object} options 30 | * @param {string} options.miniscript 31 | * @param {string} options.scriptType 32 | * @param {string} options.pkType 33 | */ 34 | function isEmpty(options) { 35 | const { miniscript, scriptType, pkType } = options; 36 | return pkg.is_empty(miniscript, scriptType, pkType); 37 | } 38 | 39 | /** 40 | * Encode into a Bitcoin Script. 41 | * @param {Object} options 42 | * @param {string} options.miniscript 43 | * @param {string} options.scriptType 44 | * @param {string} options.pkType 45 | */ 46 | function toBytes(options) { 47 | const { miniscript, scriptType, pkType } = options; 48 | return pkg.to_bytes(miniscript, scriptType, pkType); 49 | } 50 | 51 | /** 52 | * Encode into a Bitcoin Script. 53 | * @param {Object} options 54 | * @param {string} options.miniscript 55 | * @param {string} options.scriptType 56 | * @param {string} options.pkType 57 | */ 58 | function toP2SH(options) { 59 | const { miniscript, scriptType, pkType } = options; 60 | return pkg.to_p2sh(miniscript, scriptType, pkType); 61 | } 62 | 63 | /** 64 | * Encode into a Bitcoin Script. 65 | * @param {Object} options 66 | * @param {string} options.miniscript 67 | * @param {string} options.scriptType 68 | * @param {string} options.pkType 69 | */ 70 | function toV0P2WSH(options) { 71 | const { miniscript, scriptType, pkType } = options; 72 | return pkg.to_v0_p2wsh(miniscript, scriptType, pkType); 73 | } 74 | 75 | /** 76 | * Encode into a Bitcoin Script. 77 | * @param {Object} options 78 | * @param {string} options.miniscript 79 | * @param {string} options.scriptType 80 | * @param {string} options.pkType 81 | */ 82 | function isP2SH(options) { 83 | const { miniscript, scriptType, pkType } = options; 84 | return pkg.is_p2sh(miniscript, scriptType, pkType); 85 | } 86 | 87 | /** 88 | * Encode into a Bitcoin Script. 89 | * @param {Object} options 90 | * @param {string} options.miniscript 91 | * @param {string} options.scriptType 92 | * @param {string} options.pkType 93 | */ 94 | function isP2PKH(options) { 95 | const { miniscript, scriptType, pkType } = options; 96 | return pkg.is_p2pkh(miniscript, scriptType, pkType); 97 | } 98 | 99 | /** 100 | * Encode into a Bitcoin Script. 101 | * @param {Object} options 102 | * @param {string} options.miniscript 103 | * @param {string} options.scriptType 104 | * @param {string} options.pkType 105 | */ 106 | function isP2PK(options) { 107 | const { miniscript, scriptType, pkType } = options; 108 | return pkg.is_p2pk(miniscript, scriptType, pkType); 109 | } 110 | 111 | /** 112 | * Encode into a Bitcoin Script. 113 | * @param {Object} options 114 | * @param {string} options.miniscript 115 | * @param {string} options.scriptType 116 | * @param {string} options.pkType 117 | */ 118 | function isWitnessProgram(options) { 119 | const { miniscript, scriptType, pkType } = options; 120 | return pkg.is_witness_program(miniscript, scriptType, pkType); 121 | } 122 | 123 | /** 124 | * Encode into a Bitcoin Script. 125 | * @param {Object} options 126 | * @param {string} options.miniscript 127 | * @param {string} options.scriptType 128 | * @param {string} options.pkType 129 | */ 130 | function isV0P2WSH(options) { 131 | const { miniscript, scriptType, pkType } = options; 132 | return pkg.is_v0_p2wsh(miniscript, scriptType, pkType); 133 | } 134 | 135 | /** 136 | * Encode into a Bitcoin Script. 137 | * @param {Object} options 138 | * @param {string} options.miniscript 139 | * @param {string} options.scriptType 140 | * @param {string} options.pkType 141 | */ 142 | function isV0P2WPKH(options) { 143 | const { miniscript, scriptType, pkType } = options; 144 | return pkg.is_v0_p2wpkh(miniscript, scriptType, pkType); 145 | } 146 | 147 | /** 148 | * Encode into a Bitcoin Script. 149 | * @param {Object} options 150 | * @param {string} options.miniscript 151 | * @param {string} options.scriptType 152 | * @param {string} options.pkType 153 | */ 154 | function isOPRETURN(options) { 155 | const { miniscript, scriptType, pkType } = options; 156 | return pkg.is_op_return(miniscript, scriptType, pkType); 157 | } 158 | 159 | /** 160 | * Encode into a Bitcoin Script. 161 | * @param {Object} options 162 | * @param {string} options.miniscript 163 | * @param {string} options.scriptType 164 | * @param {string} options.pkType 165 | */ 166 | function isProvablyUnspendable(options) { 167 | const { miniscript, scriptType, pkType } = options; 168 | return pkg.is_provably_unspendable(miniscript, scriptType, pkType); 169 | } 170 | 171 | module.exports = { 172 | encode, 173 | len, 174 | isEmpty, 175 | toBytes, 176 | toP2SH, 177 | toV0P2WSH, 178 | isP2SH, 179 | isP2PKH, 180 | isP2PK, 181 | isWitnessProgram, 182 | isV0P2WSH, 183 | isV0P2WPKH, 184 | isOPRETURN, 185 | isProvablyUnspendable, 186 | }; 187 | -------------------------------------------------------------------------------- /lib/methods/semantic.js: -------------------------------------------------------------------------------- 1 | const pkg = require('../../pkg/miniscript_js'); 2 | 3 | /** 4 | * Encode into a Bitcoin Script. 5 | * @param {Object} options 6 | * @param {string} options.miniscript 7 | * @param {string} options.scriptType 8 | * @param {string} options.pkType 9 | */ 10 | function normalized(options) { 11 | const { miniscript, scriptType, pkType } = options; 12 | return pkg.normalized(miniscript, scriptType, pkType); 13 | } 14 | 15 | /** 16 | * Encode into a Bitcoin Script. 17 | * @param {Object} options 18 | * @param {string} options.miniscript 19 | * @param {string} options.scriptType 20 | * @param {string} options.pkType 21 | */ 22 | function isTrivial(options) { 23 | const { miniscript, scriptType, pkType } = options; 24 | return pkg.is_trivial(miniscript, scriptType, pkType); 25 | } 26 | 27 | /** 28 | * Encode into a Bitcoin Script. 29 | * @param {Object} options 30 | * @param {string} options.miniscript 31 | * @param {string} options.scriptType 32 | * @param {string} options.pkType 33 | */ 34 | function isUnsatisfiable(options) { 35 | const { miniscript, scriptType, pkType } = options; 36 | return pkg.is_unsatisfiable(miniscript, scriptType, pkType); 37 | } 38 | 39 | /** 40 | * Encode into a Bitcoin Script. 41 | * @param {Object} options 42 | * @param {string} options.miniscript 43 | * @param {string} options.scriptType 44 | * @param {string} options.pkType 45 | * @returns {Object} 46 | */ 47 | function relativeTimelocks(options) { 48 | const { miniscript, scriptType, pkType } = options; 49 | return pkg.relative_timelocks(miniscript, scriptType, pkType); 50 | } 51 | 52 | /** 53 | * Encode into a Bitcoin Script. 54 | * @param {Object} options 55 | * @param {string} options.miniscript 56 | * @param {string} options.scriptType 57 | * @param {string} options.pkType 58 | * @param {number} time 59 | * @returns {Object} 60 | */ 61 | function atAge(options, time) { 62 | const { miniscript, scriptType, pkType } = options; 63 | return pkg.at_age(miniscript, scriptType, pkType, time); 64 | } 65 | 66 | /** 67 | * Encode into a Bitcoin Script. 68 | * @param {Object} options 69 | * @param {string} options.miniscript 70 | * @param {string} options.scriptType 71 | * @param {string} options.pkType 72 | * @returns {Object} 73 | */ 74 | function nKeys(options) { 75 | const { miniscript, scriptType, pkType } = options; 76 | return pkg.n_keys(miniscript, scriptType, pkType); 77 | } 78 | 79 | /** 80 | * Encode into a Bitcoin Script. 81 | * @param {Object} options 82 | * @param {string} options.miniscript 83 | * @param {string} options.scriptType 84 | * @param {string} options.pkType 85 | * @returns {Object} 86 | */ 87 | function minimumNKeys(options) { 88 | const { miniscript, scriptType, pkType } = options; 89 | return pkg.minimum_n_keys(miniscript, scriptType, pkType); 90 | } 91 | 92 | /** 93 | * Encode into a Bitcoin Script. 94 | * @param {Object} options 95 | * @param {string} options.miniscript 96 | * @param {string} options.scriptType 97 | * @param {string} options.pkType 98 | * @returns {Object} 99 | */ 100 | function sorted(options) { 101 | const { miniscript, scriptType, pkType } = options; 102 | return pkg.sorted(miniscript, scriptType, pkType); 103 | } 104 | 105 | module.exports = { 106 | normalized, 107 | isTrivial, 108 | isUnsatisfiable, 109 | relativeTimelocks, 110 | atAge, 111 | nKeys, 112 | minimumNKeys, 113 | sorted, 114 | }; 115 | -------------------------------------------------------------------------------- /lib/miniscript.js: -------------------------------------------------------------------------------- 1 | const { ScriptType, PublicKeyType } = require('./utils/common'); 2 | 3 | const miniscript = require('./methods/miniscript'); 4 | const script = require('./methods/script'); 5 | const semantic = require('./methods/semantic'); 6 | 7 | class Miniscript { 8 | constructor(options) { 9 | let optionsCopy = {}; 10 | 11 | if (typeof options === 'object') { 12 | optionsCopy = Object.assign({}, options); 13 | } 14 | 15 | this.options = {}; 16 | this.options.miniscript = optionsCopy.miniscript; 17 | this.options.scriptType = ScriptType.Miniscript; 18 | this.options.pkType = optionsCopy.pkType; 19 | 20 | if (!Object.values(PublicKeyType).includes(this.options.pkType)) { 21 | throw new Error(`Invalid \`pkType\`, ${this.options.pkType}`); 22 | } 23 | 24 | this.miniscript = {}; 25 | this.miniscript.encode = this._miniscriptEncode.bind(this); 26 | this.miniscript.scriptSize = this._scriptSize.bind(this); 27 | this.miniscript.maxSatisfactionWitnessElements = 28 | this._maxSatisfactionWitnessElements.bind(this); 29 | this.miniscript.maxSatisfactionSize = this._maxSatisfactionSize.bind(this); 30 | 31 | this.script = {}; 32 | this.script.encode = this._scriptEncode.bind(this); 33 | this.script.len = this._len.bind(this); 34 | this.script.isEmpty = this._isEmpty.bind(this); 35 | this.script.toBytes = this._toBytes.bind(this); 36 | this.script.toP2SH = this._toP2SH.bind(this); 37 | this.script.toV0P2WSH = this._toV0P2WSH.bind(this); 38 | this.script.isP2SH = this._isP2SH.bind(this); 39 | this.script.isP2PKH = this._isP2PKH.bind(this); 40 | this.script.isP2PK = this._isP2PK.bind(this); 41 | this.script.isWitnessProgram = this._isWitnessProgram.bind(this); 42 | this.script.isV0P2WSH = this._isV0P2WSH.bind(this); 43 | this.script.isV0P2WPKH = this._isV0P2WPKH.bind(this); 44 | this.script.isOPRETURN = this._isOPRETURN.bind(this); 45 | this.script.isProvablyUnspendable = this._isProvablyUnspendable.bind(this); 46 | 47 | this.semantic = {}; 48 | this.semantic.normalized = this._normalized.bind(this); 49 | this.semantic.isTrivial = this._isTrivial.bind(this); 50 | this.semantic.isUnsatisfiable = this._isUnsatisfiable.bind(this); 51 | this.semantic.relativeTimelocks = this._relativeTimelocks.bind(this); 52 | this.semantic.atAge = this._atAge.bind(this); 53 | this.semantic.nKeys = this._nKeys.bind(this); 54 | this.semantic.minimumNKeys = this._minimumNKeys.bind(this); 55 | this.semantic.sorted = this._sorted.bind(this); 56 | } 57 | 58 | /** 59 | * Miniscript 60 | */ 61 | _miniscriptEncode() { 62 | return this.options.miniscript; 63 | } 64 | 65 | _scriptSize() { 66 | return miniscript.scriptSize(this.options); 67 | } 68 | 69 | _maxSatisfactionWitnessElements() { 70 | return miniscript.maxSatisfactionWitnessElements(this.options); 71 | } 72 | 73 | _maxSatisfactionSize() { 74 | return miniscript.maxSatisfactionSize(this.options); 75 | } 76 | 77 | /** 78 | * Script 79 | */ 80 | _scriptEncode() { 81 | return script.encode(this.options); 82 | } 83 | 84 | _len() { 85 | return script.len(this.options); 86 | } 87 | 88 | _isEmpty() { 89 | return script.isEmpty(this.options); 90 | } 91 | 92 | _toBytes() { 93 | return script.toBytes(this.options); 94 | } 95 | 96 | _toP2SH() { 97 | return script.toP2SH(this.options); 98 | } 99 | 100 | _toV0P2WSH() { 101 | return script.toV0P2WSH(this.options); 102 | } 103 | 104 | _isP2SH() { 105 | return script.isP2SH(this.options); 106 | } 107 | 108 | _isP2PKH() { 109 | return script.isP2PKH(this.options); 110 | } 111 | 112 | _isP2PK() { 113 | return script.isP2PK(this.options); 114 | } 115 | 116 | _isWitnessProgram() { 117 | return script.isWitnessProgram(this.options); 118 | } 119 | 120 | _isV0P2WSH() { 121 | return script.isV0P2WSH(this.options); 122 | } 123 | 124 | _isV0P2WSH() { 125 | return script.isV0P2WSH(this.options); 126 | } 127 | 128 | _isV0P2WPKH() { 129 | return script.isV0P2WPKH(this.options); 130 | } 131 | 132 | _isOPRETURN() { 133 | return script.isOPRETURN(this.options); 134 | } 135 | 136 | _isProvablyUnspendable() { 137 | return script.isProvablyUnspendable(this.options); 138 | } 139 | 140 | /** 141 | * Semantic 142 | */ 143 | _normalized() { 144 | return semantic.normalized(this.options); 145 | } 146 | 147 | _isTrivial() { 148 | return semantic.isTrivial(this.options); 149 | } 150 | 151 | _isUnsatisfiable() { 152 | return semantic.isUnsatisfiable(this.options); 153 | } 154 | 155 | _relativeTimelocks() { 156 | return semantic.relativeTimelocks(this.options); 157 | } 158 | 159 | _atAge(time) { 160 | return semantic.atAge(this.options, time); 161 | } 162 | 163 | _nKeys() { 164 | return semantic.nKeys(this.options); 165 | } 166 | 167 | _minimumNKeys() { 168 | return semantic.minimumNKeys(this.options); 169 | } 170 | 171 | _sorted() { 172 | return semantic.sorted(this.options); 173 | } 174 | } 175 | 176 | module.exports = Miniscript; 177 | -------------------------------------------------------------------------------- /lib/utils/common.js: -------------------------------------------------------------------------------- 1 | const ScriptType = { 2 | Miniscript: 'MINISCRIPT', 3 | Policy: 'POLICY', 4 | Script: 'SCRIPT', 5 | }; 6 | 7 | const PublicKeyType = { 8 | String: 'STRING', 9 | PublicKey: 'PUBLIC_KEY', 10 | DummyKey: 'DUMMY_KEY', 11 | }; 12 | 13 | module.exports = { 14 | ScriptType, 15 | PublicKeyType, 16 | }; 17 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "miniscript-js", 3 | "version": "0.1.2", 4 | "description": "Native Node.js bindings to rust-miniscript.", 5 | "main": "lib/index.js", 6 | "directories": { 7 | "lib": "lib" 8 | }, 9 | "scripts": { 10 | "test": "echo \"Error: no test specified\" && exit 1", 11 | "build": "CC=clang-8 wasm-pack build --target nodejs" 12 | }, 13 | "author": "nijynot ", 14 | "license": "MIT" 15 | } 16 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | use wasm_bindgen::prelude::*; 2 | 3 | #[macro_use] 4 | extern crate serde_derive; 5 | 6 | extern crate bitcoin; 7 | 8 | use std::str::FromStr; 9 | use bitcoin::Script; 10 | use miniscript::policy::{Concrete, Liftable}; 11 | use miniscript::{Miniscript, DummyKey}; 12 | use js_sys::Array; 13 | 14 | pub mod script_types { 15 | pub const MINISCRIPT: &'static str = "MINISCRIPT"; 16 | pub const POLICY: &'static str = "POLICY"; 17 | } 18 | 19 | pub mod public_key_types { 20 | pub const STRING: &'static str = "STRING"; 21 | pub const PUBLIC_KEY: &'static str = "PUBLIC_KEY"; 22 | pub const DUMMY_KEY: &'static str = "DUMMY"; 23 | } 24 | 25 | trait MiniscriptBindings { 26 | fn script_size(&self) -> Option; 27 | fn _encode(&self) -> Option