├── .editorconfig ├── .github └── workflows │ └── nodejs.yml ├── .gitignore ├── .tool-versions ├── .yarn └── releases │ └── yarn-3.2.0.cjs ├── .yarnrc.yml ├── LICENSE ├── README.md ├── build.js ├── media └── txforge-poster.jpg ├── package.json ├── src ├── casts │ ├── index.js │ ├── op-return.js │ ├── p2ms.js │ ├── p2pk.js │ ├── p2pkh.js │ ├── p2rph.js │ └── raw.js ├── classes │ ├── cast.js │ ├── forge.js │ ├── shared.js │ ├── tape.js │ └── utxo.js ├── extra │ └── r-puzzle.js ├── helpers │ ├── asm.js │ ├── index.js │ └── num.js ├── index.js └── macros │ ├── binary.js │ ├── index.js │ ├── push-tx.js │ ├── sign-tx.js │ └── varint.js ├── test ├── casts │ ├── opreturn.test.js │ ├── p2ms.test.js │ ├── p2pk.test.js │ ├── p2pkh.test.js │ ├── p2rph.test.js │ └── raw.test.js ├── classes │ ├── cast.test.js │ ├── forge.test.js │ ├── shared.test.js │ ├── tape.test.js │ └── utxo.test.js ├── extra │ └── r-puzzle.test.js ├── helpers │ ├── asm.test.js │ └── num.test.js ├── macros │ ├── binary.test.js │ ├── push-tx.test.js │ └── varint.test.js └── vectors │ └── bip69.json └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | 7 | [*.{js,json,yml}] 8 | charset = utf-8 9 | indent_style = space 10 | indent_size = 2 11 | -------------------------------------------------------------------------------- /.github/workflows/nodejs.yml: -------------------------------------------------------------------------------- 1 | # This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions 3 | 4 | name: Node.js CI 5 | 6 | on: 7 | push: 8 | branches: [ master ] 9 | pull_request: 10 | branches: [ master ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | strategy: 18 | matrix: 19 | node-version: [12.x, 14.x, 16.x, 18.x] 20 | 21 | steps: 22 | - uses: actions/checkout@v2 23 | - name: Use Node.js ${{ matrix.node-version }} 24 | uses: actions/setup-node@v1 25 | with: 26 | node-version: ${{ matrix.node-version }} 27 | - run: yarn install 28 | - run: yarn run test -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | .pnp.* 4 | .yarn/* 5 | !.yarn/patches 6 | !.yarn/plugins 7 | !.yarn/releases 8 | !.yarn/sdks 9 | !.yarn/versions 10 | 11 | dist 12 | node_modules 13 | test/demo*.js 14 | -------------------------------------------------------------------------------- /.tool-versions: -------------------------------------------------------------------------------- 1 | nodejs lts-gallium -------------------------------------------------------------------------------- /.yarnrc.yml: -------------------------------------------------------------------------------- 1 | nodeLinker: node-modules 2 | yarnPath: .yarn/releases/yarn-3.2.0.cjs 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TxForge 2 | 3 | ![Industrial strength transaction builder](https://raw.githubusercontent.com/libitx/txforge/master/media/txforge-poster.jpg) 4 | 5 | [![npm](https://img.shields.io/npm/v/txforge?color=informational)](https://www.npmjs.com/package/txforge) 6 | ![License](https://img.shields.io/github/license/libitx/txforge?color=informational) 7 | ![Build Status](https://img.shields.io/github/workflow/status/libitx/txforge/Node.js%20CI/v2) 8 | 9 | 10 | TxForge is an industrial strength transaction builder. Re-built on top of [nimble](https://github.com/runonbitcoin/nimble), TxForge 2.0 is an ultra-lightweight, ultra-flexible, essential part of any respectable Bitcoin builders' toolkit. 11 | 12 | - simple, human-friendly declarative API for composing transactions 13 | - extendable and flexible - can forge transactions with any script template imaginable 14 | - under the hood it's powered by nimble, less that 1/5 the size of [moneybutton/bsv v2](https://github.com/moneybutton/bsv) and up to 4 times as fast! 15 | - a robust library using well-tested, modern javascript 16 | 17 | *The legacy version of TxForge, powered by [bsv v2](https://github.com/moneybutton/bsv), can be found on the [legacy branch](https://github.com/libitx/txforge/tree/legacy).* 18 | 19 | ## Sponsors 20 | 21 |

Supported by:

22 |

23 | 24 | Coingeek 25 | 26 |

27 | 28 | Your sponsorship will help us continue to release and maintain software that Bitcoin businesses and developers depend on. 29 | 30 | #### 👉 [Sponsor Chronos Labs' open source work](https://www.chronoslabs.net/sponsor/) 31 | 32 | ## Upgrading 33 | 34 | If you've used previous versions of TxForge, conceptually everything is the same. However, v2.0 is a rewrite from top to bottom, powered by a new Bitcoin library, and incorporates some API changes, most notably with how Casts are defined and used. 35 | 36 | Full more details, check out the [TxForge 2 upgrade notes](https://github.com/libitx/txforge/wiki/Installing-and-upgrading-TxForge#txforge-2-upgrade-notes). 37 | 38 | ## Quick start 39 | 40 | Install TxForge with `npm` or `yarn`: 41 | 42 | ```shell 43 | npm install txforge 44 | # or 45 | yarn add txforge 46 | ``` 47 | 48 | Alternatively use in a browser via CDN: 49 | 50 | ```html 51 | 52 | 53 | 54 | 55 | 56 | ``` 57 | 58 | Grab your tools and put on your safety googles. Lets forge a transaction... it's hammer time! 59 | 60 | ```js 61 | import { forgeTx, toUTXO, casts } from 'txforge' 62 | 63 | // We'll use these Casts in our transaction 64 | const { P2PKH, OpReturn } = casts 65 | 66 | // You'll need UTXOs to fund a transaction. Use the `toUTXO` helper to turn 67 | // your UTXO data into the required objects. 68 | const utxo = toUTXO({ 69 | txid, // utxo transaction id 70 | vout, // utxo output index 71 | satoshis, // utxo amount 72 | script // utxo lock script 73 | }) 74 | 75 | // Forge a transaction 76 | const tx = forgeTx({ 77 | inputs: [ 78 | P2PKH.unlock(utxo, { privkey: myPrivateKey }) 79 | ], 80 | outputs: [ 81 | P2PKH.lock(5000, { address: '1DBz6V6CmvjZTvfjvWpvvwuM1X7GkRmWEq' }), 82 | OpReturn.lock(0, { data: ['meta', '1DBz6V6CmvjZTvfjvWpvvwuM1X7GkRmWEq', txid] }) 83 | ], 84 | change: { address: '1Nro9WkpaKm9axmcfPVp79dAJU1Gx7VmMZ' } 85 | }) 86 | 87 | // And behold! Forged by the Gods and found by a King - a transaction is born. 88 | console.log(tx.toHex()) 89 | ``` 90 | 91 | And that's it. Really. One function that returns a fully built and signed transaction, ready to send off to your favourite transaction processor. 92 | 93 | But there's more, much more. When you're ready, grab a coffee and dive into the following to discover how TxForge can be used to lock and unlock any combination of script template you can imagine. 94 | 95 | - [Understanding Casts](https://github.com/libitx/txforge/wiki/Understanding-Casts) 96 | - [Creating custom Casts](https://github.com/libitx/txforge/wiki/Creating-custom-Casts) 97 | - [Building Scripts with TxForge](https://github.com/libitx/txforge/wiki/Building-scripts-with-the-Tape-API) 98 | 99 | ## License 100 | 101 | TxForge is open source and released under the [Apache-2 License](https://github.com/libitx/txforge/blob/master/LICENSE). 102 | 103 | Copyright (c) 2020-2022 Chronos Labs Ltd. 104 | -------------------------------------------------------------------------------- /build.js: -------------------------------------------------------------------------------- 1 | import esbuild from 'esbuild' 2 | import GlobalsPlugin from 'esbuild-plugin-globals' 3 | 4 | const makeAllPackagesExternalPlugin = { 5 | name: 'make-all-packages-external', 6 | setup(build) { 7 | let filter = /^[^.\/]|^\.[^.\/]|^\.\.[^\/]/ // Must not start with "/" or "./" or "../" 8 | build.onResolve({ filter }, args => ({ path: args.path, external: true })) 9 | }, 10 | } 11 | 12 | esbuild.build({ 13 | entryPoints: ['src/index.js'], 14 | outfile: 'dist/txforge.min.js', 15 | globalName: 'txforge', 16 | bundle: true, 17 | format: 'iife', 18 | platform: 'browser', 19 | target: 'es6', 20 | minify: true, 21 | keepNames: true, 22 | sourcemap: true, 23 | define: { 24 | VARIANT: 'browser' 25 | }, 26 | plugins: [ 27 | GlobalsPlugin({ 28 | 'crypto': 'null', 29 | '@runonbitcoin/nimble': 'nimble' 30 | }) 31 | ] 32 | }) 33 | 34 | esbuild.build({ 35 | entryPoints: ['src/index.js'], 36 | outfile: 'dist/txforge.bundled.min.js', 37 | globalName: 'txforge', 38 | bundle: true, 39 | format: 'iife', 40 | platform: 'browser', 41 | target: 'es6', 42 | minify: true, 43 | keepNames: true, 44 | sourcemap: true, 45 | define: { 46 | VARIANT: 'browser' 47 | }, 48 | plugins: [ 49 | GlobalsPlugin({ 50 | 'crypto': 'null' 51 | }) 52 | ] 53 | }) 54 | 55 | esbuild.build({ 56 | entryPoints: [ 57 | 'src/index.js', 58 | 'src/extra/r-puzzle.js' 59 | ], 60 | outdir: 'dist/cjs', 61 | outExtension: { 62 | '.js': '.cjs' 63 | }, 64 | bundle: true, 65 | format: 'cjs', 66 | platform: 'node', 67 | target: 'node10', 68 | keepNames: true, 69 | plugins: [ 70 | makeAllPackagesExternalPlugin 71 | ] 72 | }) 73 | -------------------------------------------------------------------------------- /media/txforge-poster.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/libitx/txforge/9e94780871286d0c9653170204b7b5eaeea4f61a/media/txforge-poster.jpg -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "txforge", 3 | "version": "2.1.0", 4 | "description": "Modern Bitcoin transaction builder, capable of supporting any non-standard and custom script type.", 5 | "keywords": [ 6 | "bitcoin", 7 | "bsv", 8 | "transaction", 9 | "builder", 10 | "nimble" 11 | ], 12 | "author": "libitx", 13 | "license": "Apache-2.0", 14 | "repository": "https://github.com/libitx/txforge", 15 | "type": "module", 16 | "exports": { 17 | ".": { 18 | "require": "./dist/cjs/index.cjs", 19 | "import": "./src/index.js" 20 | }, 21 | "./*": { 22 | "require": "./dist/cjs/*.cjs", 23 | "import": "./src/*.js" 24 | } 25 | }, 26 | "main": "./dist/cjs/index.cjs", 27 | "browser": "./dist/txforge.min.js", 28 | "unpkg": "./dist/txforge.min.js", 29 | "files": [ 30 | "dist", 31 | "src" 32 | ], 33 | "packageManager": "yarn@3.2.0", 34 | "scripts": { 35 | "build": "yarn clean && node build.js", 36 | "clean": "rm -rf dist", 37 | "test": "ava test/**/*.test.js" 38 | }, 39 | "dependencies": { 40 | "@runonbitcoin/nimble": "^1.0.13" 41 | }, 42 | "devDependencies": { 43 | "ava": "^4.2.0", 44 | "esbuild": "^0.14.38", 45 | "esbuild-plugin-globals": "^0.1.1" 46 | }, 47 | "ava": { 48 | "concurrency": 4 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/casts/index.js: -------------------------------------------------------------------------------- 1 | export { OpReturn } from './op-return.js' 2 | export { P2MS } from './p2ms.js' 3 | export { P2PK } from './p2pk.js' 4 | export { P2PKH } from './p2pkh.js' 5 | export { P2RPH } from './p2rph.js' 6 | export { Raw } from './raw.js' 7 | -------------------------------------------------------------------------------- /src/casts/op-return.js: -------------------------------------------------------------------------------- 1 | import nimble from '@runonbitcoin/nimble' 2 | import { Cast } from '../classes/cast.js' 3 | 4 | const { OP_FALSE, OP_RETURN } = nimble.constants.opcodes 5 | 6 | /** 7 | * OP_RETURN outputs are frequently used for publishing arbitrary data on-chain. 8 | * 9 | * As this is generally used for creating unspendable outputs, no unlocking 10 | * script is defined on this contract. 11 | * 12 | * ## Lock params 13 | * 14 | * - `data` - A single buffer or string, or array of buffers/strings. 15 | * 16 | * ## Examples 17 | * 18 | * ``` 19 | * OpReturn.lock(0, { data: 'hello world' }) 20 | * 21 | * OpReturn.lock(0, { data: ['hello', 'world'] }) 22 | * ``` 23 | */ 24 | export class OpReturn extends Cast { 25 | lockingScript({ data }) { 26 | this.script 27 | .push(OP_FALSE) 28 | .push(OP_RETURN) 29 | .push(data) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/casts/p2ms.js: -------------------------------------------------------------------------------- 1 | import nimble from '@runonbitcoin/nimble' 2 | import { Cast } from '../classes/cast.js' 3 | import { num } from '../helpers/index.js' 4 | import { signTx } from '../macros/index.js' 5 | 6 | const { OP_0, OP_CHECKMULTISIG } = nimble.constants.opcodes 7 | 8 | /** 9 | * Pay to Multi Sig 10 | * 11 | * P2MS scripts are used to lock Bitcoin to multiple public keys. The Bitcoin 12 | * can later be unlocked by signing with the specified threshold of 13 | * corresponding private keys. 14 | * 15 | * ## Lock params 16 | * 17 | * - `pubkeys` - Array of public keys 18 | * - `threshold` - Integer of required signatures 19 | * 20 | * ## Unlock params 21 | * 22 | * - `privkeys` - Array of private keys 23 | * 24 | * ## Example 25 | * 26 | * ``` 27 | * P2MS.lock(1000, { pubkeys: [pub1, pub2, pub3], threshold: 2 }) 28 | * 29 | * P2MS.unlock(utxo, { privkey: [priv1, priv2] }) 30 | * ``` 31 | */ 32 | export class P2MS extends Cast { 33 | init() { 34 | if (this.mode === 'lock') validateLock(this.params) 35 | if (this.mode === 'unlock') validateUnlock(this.params) 36 | } 37 | 38 | lockingScript({ pubkeys, threshold }) { 39 | this.script 40 | .push(num(threshold)) 41 | .apply(pushPubkeys, [pubkeys]) 42 | .push(num(pubkeys.length)) 43 | .push(OP_CHECKMULTISIG) 44 | } 45 | 46 | unlockingScript({ privkeys }) { 47 | this.script 48 | .push(OP_0) 49 | .apply(pushSigs, [privkeys]) 50 | } 51 | } 52 | 53 | function pushPubkeys(pubkeys) { 54 | pubkeys.forEach(pubkey => { 55 | this.script.push(pubkey) 56 | }) 57 | } 58 | 59 | function pushSigs(privkeys) { 60 | privkeys.forEach(privkey => { 61 | this.script.apply(signTx, [privkey]) 62 | }) 63 | } 64 | 65 | function validateLock({ pubkeys, threshold } = {}) { 66 | if (!(Array.isArray(pubkeys) && pubkeys.every(k => k.point))) { 67 | throw new Error('P2MS lock must be created with array of `pubkeys`') 68 | } 69 | 70 | if (!Number.isInteger(threshold)) { 71 | throw new Error('P2MS lock must be created with `threshold` integer') 72 | } 73 | } 74 | 75 | function validateUnlock({ privkeys } = {}) { 76 | if (!(Array.isArray(privkeys) && privkeys.every(k => typeof k.toPublicKey === 'function'))) { 77 | throw new Error('P2MS unlock must be created with array of `privkeys`') 78 | } 79 | } -------------------------------------------------------------------------------- /src/casts/p2pk.js: -------------------------------------------------------------------------------- 1 | import nimble from '@runonbitcoin/nimble' 2 | import { Cast } from '../classes/cast.js' 3 | import { signTx } from '../macros/index.js' 4 | 5 | const { OP_CHECKSIG } = nimble.constants.opcodes 6 | 7 | /** 8 | * Pay to Public Key 9 | * 10 | * P2PK scripts are used to lock Bitcoin to a public key. The Bitcoin can later 11 | * be unlocked using the corresponding private key. 12 | * 13 | * ## Lock params 14 | * 15 | * - `pubkey` - Public key 16 | * 17 | * ## Unlock params 18 | * 19 | * - `privkey` - Private key 20 | * 21 | * ## Examples 22 | * 23 | * ``` 24 | * P2PK.lock(1000, { pubkey }) 25 | * 26 | * P2PK.unlock(utxo, { privkey }) 27 | * ``` 28 | */ 29 | export class P2PK extends Cast { 30 | init() { 31 | if (this.mode === 'lock') validateLock(this.params) 32 | if (this.mode === 'unlock') validateUnlock(this.params) 33 | } 34 | 35 | lockingScript({ pubkey }) { 36 | this.script 37 | .push(pubkey) 38 | .push(OP_CHECKSIG) 39 | } 40 | 41 | unlockingScript({ privkey }) { 42 | this.script.apply(signTx, [privkey]) 43 | } 44 | } 45 | 46 | function validateLock({ pubkey } = {}) { 47 | if (!(pubkey && pubkey.point)) { 48 | throw new Error('P2PK unlock must be created with valid `pubkey`') 49 | } 50 | } 51 | 52 | function validateUnlock({ privkey } = {}) { 53 | if (!(privkey && typeof privkey.toPublicKey === 'function')) { 54 | throw new Error('P2PK unlock must be created with valid `privkey`') 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/casts/p2pkh.js: -------------------------------------------------------------------------------- 1 | import nimble from '@runonbitcoin/nimble' 2 | import { Cast } from '../classes/cast.js' 3 | import { signTx } from '../macros/index.js' 4 | 5 | const { Address } = nimble.classes 6 | const { OP_DUP, OP_HASH160, OP_EQUALVERIFY, OP_CHECKSIG } = nimble.constants.opcodes 7 | 8 | /** 9 | * Pay to Public Key Hash 10 | * 11 | * P2PKH scripts are used to lock Bitcoin to an address. The Bitcoin can later 12 | * be unlocked using the private key corresponding to the address. 13 | * 14 | * ## Lock params 15 | * 16 | * - `address` - address instance or string 17 | * 18 | * ## Unlock params 19 | * 20 | * - `privkey` - Private key 21 | * 22 | * ## Examples 23 | * 24 | * ``` 25 | * P2PKH.lock(1000, { address }) 26 | * 27 | * P2PKH.unlock(utxo, { privkey }) 28 | * ``` 29 | */ 30 | export class P2PKH extends Cast { 31 | init(params) { 32 | if (typeof params.address === 'string') { 33 | this.params.address = Address.from(params.address) 34 | } 35 | 36 | if (this.mode === 'lock') validateLock(this.params) 37 | if (this.mode === 'unlock') validateUnlock(this.params) 38 | } 39 | 40 | lockingScript({ address }) { 41 | this.script 42 | .push(OP_DUP) 43 | .push(OP_HASH160) 44 | .push(address.pubkeyhash) 45 | .push(OP_EQUALVERIFY) 46 | .push(OP_CHECKSIG) 47 | } 48 | 49 | unlockingScript({ privkey }) { 50 | this.script 51 | .apply(signTx, [privkey]) 52 | .push(privkey.toPublicKey()) 53 | } 54 | } 55 | 56 | function validateLock({ address } = {}) { 57 | if (!(address && address.pubkeyhash)) { 58 | throw new Error('P2PKH lock must be created with valid `address`') 59 | } 60 | } 61 | 62 | function validateUnlock({ privkey } = {}) { 63 | if (!(privkey && typeof privkey.toPublicKey === 'function')) { 64 | throw new Error('P2PKH unlock must be created with valid `privkey`') 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/casts/p2rph.js: -------------------------------------------------------------------------------- 1 | import nimble from '@runonbitcoin/nimble' 2 | import { Cast } from '../classes/cast.js' 3 | import { asm } from '../helpers/index.js' 4 | import { signTx, signTxWithK } from '../macros/index.js' 5 | 6 | const { PrivateKey } = nimble.classes 7 | const { isBuffer, generatePrivateKey, sha256ripemd160 } = nimble.functions 8 | 9 | /** 10 | * Pay to R-Puzzle Hash 11 | * 12 | * P2RPH scripts are used to lock Bitcoin to a hash puzzle based on the R value 13 | * of an ECDSA signature. The Bitcoin can later be unlocked with knowledge of 14 | * the corresponding K value used in that signature. 15 | * 16 | * The technique allows for the spending party to sign the unlocking script 17 | * using any private key. 18 | * 19 | * ## Lock params 20 | * 21 | * - `r` - R value 22 | * 23 | * ## Unlock params 24 | * 25 | * - `k` - K value 26 | * - `privkey` - Optional private key to sign with 27 | * 28 | * ## Examples 29 | * 30 | * TxForge offers an additional module with helpers to generate K and R values. 31 | * 32 | * ``` 33 | * import { generateK, calculateR } from 'txforge/extra/r-puzzle 34 | * 35 | * const k = generateK() 36 | * const r = calculateR(k) 37 | * 38 | * P2RPH.lock(1000, { r }) 39 | * 40 | * P2RPH.unlock(utxo, { k }) 41 | * ``` 42 | */ 43 | export class P2RPH extends Cast { 44 | init(params) { 45 | if (this.mode === 'unlock' && typeof params.privkey === 'undefined') { 46 | this.params.privkey = PrivateKey.fromRandom() 47 | } 48 | 49 | if (this.mode === 'lock') validateLock(this.params) 50 | if (this.mode === 'unlock') validateUnlock(this.params) 51 | } 52 | 53 | lockingScript({ r }) { 54 | this.script 55 | .push(asm('OP_OVER OP_3 OP_SPLIT OP_NIP OP_1 OP_SPLIT OP_SWAP OP_SPLIT OP_DROP OP_HASH160')) 56 | .push(sha256ripemd160(r)) 57 | .push(asm('OP_EQUALVERIFY OP_TUCK OP_CHECKSIGVERIFY OP_CHECKSIG')) 58 | } 59 | 60 | unlockingScript({ k, privkey }) { 61 | this.script 62 | .apply(signTx, [privkey]) 63 | .apply(signTxWithK, [privkey, k]) 64 | .push(privkey.toPublicKey()) 65 | } 66 | } 67 | 68 | function validateLock({ r } = {}) { 69 | if (!isBuffer(r) || ![32, 33].includes(r.length)) { 70 | throw new Error('P2RPH lock must be created with valid `r` buffer') 71 | } 72 | } 73 | 74 | function validateUnlock({ k } = {}) { 75 | if (!isBuffer(k) || k.length !== 32) { 76 | throw new Error('P2RPH unlock must be created with valid `k` buffer') 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/casts/raw.js: -------------------------------------------------------------------------------- 1 | import nimble from '@runonbitcoin/nimble' 2 | import { Cast } from '../classes/cast.js' 3 | 4 | const { Script } = nimble.classes 5 | 6 | /** 7 | * The Raw cast provides a way to use an existing script with TxForge. 8 | * 9 | * * ## Lock params 10 | * 11 | * - `script` - Script instance or hex-encoded string 12 | * 13 | * ## Unlock params 14 | * 15 | * - `script` - Script instance or hex-encoded string 16 | * 17 | * ## Examples 18 | * 19 | * ``` 20 | * Raw.lock(1000, { script: '76a91412ab8dc588ca9d5787dde7eb29569da63c3a238c88ac' }) 21 | * ``` 22 | */ 23 | export class Raw extends Cast { 24 | init(params) { 25 | if (typeof params.script === 'string') { 26 | this.params.script = Script.fromString(params.script) 27 | } 28 | 29 | if (!(this.params.script && typeof this.params.script.toBuffer === 'function')) { 30 | throw new Error('Raw cast must be created with valid `script`') 31 | } 32 | } 33 | 34 | lockingScript({ script }) { 35 | this.script.push(script.chunks) 36 | } 37 | 38 | unlockingScript({ script }) { 39 | this.script.push(script.chunks) 40 | } 41 | } 42 | 43 | -------------------------------------------------------------------------------- /src/classes/cast.js: -------------------------------------------------------------------------------- 1 | import nimble from '@runonbitcoin/nimble' 2 | import { forgeTx } from './forge.js' 3 | import { Tape, toScript } from './tape.js' 4 | import { getUTXO } from './utxo.js' 5 | import { getOpt } from './shared.js' 6 | 7 | const { Transaction } = nimble.classes 8 | const { evalScript } = nimble.functions 9 | 10 | /** 11 | * Casts are Bitcoin script templates. 12 | * 13 | * A Bitcoin transaction consists of two sides: inputs and outputs. 14 | * 15 | * An output contains a locking script which locks a number of satoshis to a 16 | * puzzle. Inputs contain an unlocking script which provides the solution to a 17 | * previous output's puzzle. 18 | * 19 | * A class that extends from `Cast` defines both `lockingScript` and 20 | * `unlockingScript` functions, and so defines the puzzle coins are locked to 21 | * and the solution for unlocking and spending those coins again. Casts have a 22 | * simple API and it is trivial to add your own helpers and macros to create 23 | * more complex script templates. 24 | * 25 | * ## Defining a Cast 26 | * 27 | * The following example implements a simple hash puzzle where coins can be 28 | * locked to a secret and then unlocked with the same secret, without the secret 29 | * being revealed onchain. 30 | * 31 | * ``` 32 | * import { Cast } from 'txforge' 33 | * import nimble from '@runonbitcoin/nimble' 34 | * 35 | * const { sha256, sha256d } = nimble.functions 36 | * 37 | * class HashPuzzle extends Cast { 38 | * lockingScript({ secret }) { 39 | * this.script 40 | * .push(opcodes.OP_SHA256) 41 | * .push(sha256d(secret)) 42 | * .push(opcodes.OP_EQUAL) 43 | * } 44 | * 45 | * unlockingScript({ secret }) { 46 | * this.script.push(sha256(secret)) 47 | * } 48 | * } 49 | * ``` 50 | * 51 | * ## Using a Cast 52 | * 53 | * The following example shows the locking and subsequent unlocking of a coin 54 | * in two separate transactions. 55 | * 56 | * ``` 57 | * import { forgeTx, getUtxo } from 'txforge' 58 | * 59 | * const secret = 'my super sEcReT password!!!1' 60 | * 61 | * const tx1 = forgeTx({ 62 | * inputs: [...], 63 | * outputs: [ 64 | * HashPuzzle.lock(1000, { secret }) 65 | * ] 66 | * }) 67 | * 68 | * const utxo = getUtxo(tx1, 0) 69 | * 70 | * const tx2 = forgeTx({ 71 | * inputs: [ 72 | * HashPuzzle.unlock(utxo, { secret }) 73 | * ], 74 | * outputs: [...] 75 | * }) 76 | * ``` 77 | * 78 | * ## Testing Casts 79 | * 80 | * TxForge provides a built-in way of testing and simulating casts. 81 | * 82 | * ``` 83 | * const secret = 'my super sEcReT password!!!1' 84 | * 85 | * // 1st arg is params given to `lockingScript` 86 | * // 2nd arg is params given to `unlockingScript` 87 | * HashPuzzle.simulate({ secret }, { secret }) 88 | * ``` 89 | */ 90 | export class Cast { 91 | constructor(mode, { params, opts, satoshis, utxo }) { 92 | this.mode = mode 93 | this.params = params 94 | this.opts = opts 95 | this.satoshis = satoshis 96 | this.utxo = utxo 97 | this.script = new Tape(this) 98 | this.ctx = null 99 | this.init(params, opts) 100 | } 101 | 102 | /** 103 | * Locks the specified number of satoshis using the given parameters. 104 | * 105 | * @param {number} satoshis Number of satoshis to lock 106 | * @param {object} params Lock parameters 107 | * @param {object} opts Lock options 108 | * @returns {Cast} 109 | */ 110 | static lock(satoshis, params, opts = {}) { 111 | return new this('lock', { satoshis, params, opts }) 112 | } 113 | 114 | /** 115 | * Locks the specified utxo using the given parameters. 116 | * 117 | * @param {UTXO} utxo Locked UTXO 118 | * @param {object} params Unlock parameters 119 | * @param {object} opts Unlock options 120 | * @returns {Cast} 121 | */ 122 | static unlock(utxo, params, opts = {}) { 123 | return new this('unlock', { utxo, params, opts }) 124 | } 125 | 126 | /** 127 | * Simulates the Cast using the given lock and unlock parameters. Returns a 128 | * `vm` object from nimble's `evalScript` function. 129 | * 130 | * @param {object} params Lock parameters 131 | * @param {object} params Unlock parameters 132 | * @returns {object} evalScript vm object 133 | */ 134 | static simulate(lockParams = {}, unlockParams = {}) { 135 | const lockTx = forgeTx({ 136 | outputs: [this.lock(1000, lockParams)] 137 | }) 138 | 139 | const utxo = getUTXO(lockTx, 0) 140 | 141 | const tx = forgeTx({ 142 | inputs: [this.unlock(utxo, unlockParams)] 143 | }) 144 | 145 | return evalScript( 146 | tx.inputs[0].script, 147 | lockTx.outputs[0].script, 148 | tx, 149 | 0, 150 | lockTx.outputs[0].satoshis, 151 | ) 152 | } 153 | 154 | /** 155 | * Casts may implement the `init()` hook. This is useful for setting defaults 156 | * and coercing and/or validating the received parameters. 157 | * 158 | * @param {object} params Lock or unlock parameters 159 | * @param {object} opts Lock or unlock options 160 | * @returns {void} 161 | */ 162 | init(params, opts) {} 163 | 164 | /** 165 | * The `lockingScript()` hook should be implemented to define the puzzle which 166 | * locks the coins. 167 | * 168 | * Refer to the {@link Tape} api. 169 | * 170 | * @param {object} params Lock or unlock parameters 171 | * @param {object} opts Lock or unlock options 172 | * @returns {void} 173 | */ 174 | lockingScript(params, opts) {} 175 | 176 | /** 177 | * The `unlockingScript()` hook should be implemented to define the solution 178 | * to the puzzle under which coins are locked. 179 | * 180 | * Refer to the {@link Tape} api. 181 | * 182 | * @param {object} params Lock or unlock parameters 183 | * @param {object} opts Lock or unlock options 184 | * @returns {void} 185 | */ 186 | unlockingScript(params, opts) {} 187 | 188 | /** 189 | * Compiles and returns the script. 190 | * 191 | * @returns {nimble.Script} 192 | */ 193 | toScript() { 194 | this.script = new Tape(this) 195 | if (this.mode === 'lock') { 196 | this.lockingScript(this.params, this.opts) 197 | } else { 198 | this.unlockingScript(this.params, this.opts) 199 | } 200 | 201 | return toScript(this.script) 202 | } 203 | 204 | /** 205 | * Compiles the script and returns a transaction Input. Only available to 206 | * unlocking Casts. 207 | * 208 | * @returns {nimble.Transaction.Input} 209 | */ 210 | toInput() { 211 | if (this.mode !== 'unlock') { 212 | throw new Error('invalid mode. `toInput()` only available in `unlock` mode.') 213 | } 214 | 215 | const { txid, vout, output } = this.utxo 216 | const script = this.toScript() 217 | const sequence = getOpt(this.opts, ['sequence'], 0xFFFFFFFF) 218 | 219 | return new Transaction.Input(txid, vout, script, sequence, output) 220 | } 221 | 222 | /** 223 | * Compiles the script and returns a transaction Output. Only available to 224 | * locking Casts. 225 | * 226 | * @returns {nimble.Transaction.Output} 227 | */ 228 | toOutput() { 229 | if (this.mode !== 'lock') { 230 | throw new Error('invalid mode. `toOutput()` only available in `lock` mode.') 231 | } 232 | 233 | const script = this.toScript() 234 | return new Transaction.Output(script, this.satoshis) 235 | } 236 | 237 | /** 238 | * Sets the transaction context on the cast. 239 | * 240 | * @param {nimble.Transaction} tx Current transaction 241 | * @param {number} vin Input index 242 | * @returns {void} 243 | */ 244 | setCtx(tx, vin) { 245 | this.ctx = { tx, vin } 246 | } 247 | } 248 | 249 | /** 250 | * Checks if the given class extends from Cast. 251 | * 252 | * @param {class} cast Cast class 253 | * @returns {boolean} 254 | */ 255 | export function isCast(cast) { 256 | return cast && 257 | Object.getPrototypeOf(cast.constructor).name === 'Cast' && 258 | (typeof cast.lockingScript === 'function' || typeof cast.unlockingScript === 'function') 259 | } 260 | -------------------------------------------------------------------------------- /src/classes/forge.js: -------------------------------------------------------------------------------- 1 | import nimble from '@runonbitcoin/nimble' 2 | import { isCast } from './cast.js' 3 | import { P2PKH, Raw } from '../casts/index.js' 4 | 5 | const { BufferWriter, Transaction } = nimble.classes 6 | const { writeVarint } = nimble.functions 7 | 8 | const defaults = { 9 | rates: { data: 50, standard: 50 }, 10 | sort: false, 11 | } 12 | 13 | /** 14 | * Forge is an industrial strength transaction builder. 15 | * 16 | * Use Casts to forge transactions of any shape and size, using any script 17 | * template imaginable. 18 | * 19 | * @see createForge 20 | * @see forgeTx 21 | */ 22 | export class Forge { 23 | constructor({ inputs, outputs, change, locktime, options } = {}) { 24 | this.inputs = [] 25 | this.outputs = [] 26 | this.changeScript = null 27 | this.locktime = 0 28 | this.options = { 29 | ...defaults, 30 | ...options 31 | } 32 | 33 | if (inputs) this.addInput(inputs) 34 | if (outputs) this.addOutput(outputs) 35 | 36 | if (Array.isArray(change)) { 37 | this.changeTo(...change) 38 | } else if (typeof change === 'object') { 39 | this.changeTo(change) 40 | } 41 | 42 | if (Number.isInteger(locktime)) { 43 | this.locktime = locktime 44 | } 45 | } 46 | 47 | get inputSum() { 48 | return this.inputs.reduce((sum, i) => sum + i.utxo.satoshis || 0, 0) 49 | } 50 | 51 | get outputSum() { 52 | return this.outputs.reduce((sum, o) => sum + o.satoshis, 0) 53 | } 54 | 55 | /** 56 | * Adds the given unlocking Cast (or array of Casts) to the forge. 57 | * 58 | * @param {Cast|Cast[]} cast Unlocking cast(s) 59 | * @returns {Forge} 60 | */ 61 | addInput(cast) { 62 | if (Array.isArray(cast)) { 63 | cast.forEach(c => this.addInput(c)) 64 | return this 65 | } 66 | 67 | if (isCast(cast) && cast.mode === 'unlock') { 68 | this.inputs.push(cast) 69 | } else { 70 | throw new Error('invalid input not added') 71 | } 72 | 73 | return this 74 | } 75 | 76 | /** 77 | * Adds the given locking Cast (or array of Casts) to the forge. 78 | * 79 | * @param {Cast|Cast[]} cast Locking cast(s) 80 | * @returns {Forge} 81 | */ 82 | addOutput(cast) { 83 | if (Array.isArray(cast)) { 84 | cast.forEach(c => this.addOutput(c)) 85 | return this 86 | } 87 | 88 | if (isCast(cast) && cast.mode === 'lock') { 89 | this.outputs.push(cast) 90 | } else { 91 | throw new Error('invalid output not added') 92 | } 93 | 94 | return this 95 | } 96 | 97 | /** 98 | * Sets the forge changeScript based on the given Cast class and params. 99 | * The first argument can be omitted if the params contains an `address` or 100 | * `script` property - in such cases the `P2PKH` or `Raw` Casts are used to 101 | * create the changeScript. 102 | * 103 | * ## Examples 104 | * 105 | * ``` 106 | * // Sets a P2PK change script 107 | * forge.changeTo(P2PK, { pubkey }) 108 | * 109 | * // Will automatically use P2PKH 110 | * forge.changeTo({ address }) 111 | * 112 | * // Will automatically use Raw 113 | * forge.changeTo({ script }) 114 | * ``` 115 | * 116 | * @param {class|object} classOrParams Cast class or lock parameters 117 | * @param {object?} params Lock parameters 118 | * @returns 119 | */ 120 | changeTo(classOrParams, params) { 121 | if (Object.getPrototypeOf(classOrParams).name === 'Cast') { 122 | this.changeScript = classOrParams.lock(0, params || {}).toScript() 123 | } else if (!!classOrParams.address) { 124 | this.changeScript = P2PKH.lock(0, classOrParams).toScript() 125 | } else if (!!classOrParams.script) { 126 | this.changeScript = Raw.lock(0, classOrParams).toScript() 127 | } else { 128 | throw new Error('invalid change params. must give cast class and lock params') 129 | } 130 | 131 | return this 132 | } 133 | 134 | /** 135 | * Calculates the fee required for miners to accept the transaction (according 136 | * to the configured rates). 137 | * 138 | * @param {object?} rates Miner rates 139 | */ 140 | calcRequiredFee(rates = this.options.rates) { 141 | const inScripts = this.inputs.map(i => i.toScript()) 142 | const outScripts = this.outputs.map(o => o.toScript()) 143 | 144 | // Initially consider all bytes standard 145 | const bytes = { 146 | standard: [ 147 | 8, 148 | varintSize(inScripts.length), 149 | varintSize(outScripts.length), 150 | inScripts.reduce((sum, script) => sum + inputSize(script), 0), 151 | outScripts.reduce((sum, script) => sum + outputSize(script), 0) 152 | ].reduce((sum, i) => sum + i, 0), 153 | data: 0 154 | } 155 | 156 | // Detect data outputs and adjust bytes 157 | outScripts 158 | .filter(s => /^006a/.test(s.toHex())) 159 | .forEach(s => { 160 | const len = outputSize(s) 161 | bytes.standard -= len 162 | bytes.data += len 163 | }) 164 | 165 | // Calculate the fee for both standard and date bytes 166 | const fee = ['standard', 'data'].reduce((sum, type) => { 167 | const rate = Number.isInteger(rates) ? rates : rates[type] 168 | return sum + (bytes[type] * rate / 1000) 169 | }, 0) 170 | 171 | return Math.ceil(fee) 172 | } 173 | 174 | /** 175 | * Sorts the inputs and outputs according to 176 | * {@link https://github.com/bitcoin/bips/blob/master/bip-0069.mediawiki BIP-69}. 177 | * 178 | * BIP-69 defines deterministic lexographical indexing of transaction inputs 179 | * and outputs. 180 | * 181 | * @returns {Forge} 182 | */ 183 | sort() { 184 | this.inputs.sort((a, b) => { 185 | return a.utxo.txid.localeCompare(b.utxo.txid) || (a.utxo.vout - b.utxo.vout) 186 | }) 187 | 188 | this.outputs.sort((a, b) => { 189 | return (a.satoshis - b.satoshis) || a.toScript().toHex().localeCompare(b.toScript().toHex()) 190 | }) 191 | 192 | return this 193 | } 194 | 195 | /** 196 | * Builds and returns a signed transaction. 197 | * 198 | * @returns {nimble.Transaction} 199 | */ 200 | toTx() { 201 | if (this.options.sort) { 202 | this.sort() 203 | } 204 | 205 | const tx = new Transaction() 206 | 207 | // First pass populate inputs with zero'd sigs 208 | this.inputs.forEach(cast => tx.input(cast.toInput())) 209 | this.outputs.forEach(cast => tx.output(cast.toOutput())) 210 | 211 | // If changeScript exists, calculate the change and add to tx 212 | if (this.changeScript) { 213 | const rates = this.options.rates 214 | const rate = Number.isInteger(rates) ? rates : rates.standard 215 | const fee = this.calcRequiredFee() 216 | const extraFee = Math.ceil(outputSize(this.changeScript) * rate / 1000) 217 | const change = (this.inputSum - this.outputSum) - (fee + extraFee) 218 | if (change > 0) { 219 | tx.output(new Transaction.Output(this.changeScript, change, tx)) 220 | } 221 | } 222 | 223 | if (this.locktime > 0) { 224 | tx.locktime = this.locktime 225 | } 226 | 227 | // Second pass replaces signed inputs 228 | this.inputs.forEach((cast, vin) => { 229 | cast.setCtx(tx, vin) 230 | tx.inputs[vin].script = cast.toScript() 231 | }) 232 | 233 | return tx 234 | } 235 | } 236 | 237 | /** 238 | * Creates and returns a forge instance from the given params. 239 | * 240 | * ## Examples 241 | * 242 | * ``` 243 | * createForge({ 244 | * inputs: [ 245 | * P2PKH.unlock(utxo, { privkey }) 246 | * ], 247 | * outputs: [ 248 | * P2PKH.lock(10000, { address }) 249 | * ], 250 | * change: { address: changeAddress } 251 | * }) 252 | * ``` 253 | * 254 | * @param {cast[]} params.inputs Array of unlocking casts 255 | * @param {cast[]} params.outputs Array of locking casts 256 | * @param {[class, object]|object?} params.change Change Cast class and lock parameters 257 | * @param {number?} params.locktime Transaction locktime integer 258 | * @param {object?} params.options.rates Miner fee quote 259 | * @param {boolean?} params.options.sort Automatically sort using BIP-69 260 | * @returns {Cast} 261 | */ 262 | export function createForge(params = {}) { 263 | return new Forge(params) 264 | } 265 | 266 | /** 267 | * As {@link createForge} but returns built and signed transaction. 268 | * 269 | * ``` 270 | * forgeTx({ 271 | * inputs: [ 272 | * P2PKH.unlock(utxo, { privkey }) 273 | * ], 274 | * outputs: [ 275 | * P2PKH.lock(10000, { address }) 276 | * ], 277 | * change: { address: changeAddress } 278 | * }) 279 | * ``` 280 | * 281 | * @param {cast[]} params.inputs Array of unlocking casts 282 | * @param {cast[]} params.outputs Array of locking casts 283 | * @param {[class, object]|object?} params.change Change Cast class and lock parameters 284 | * @param {number?} params.locktime Transaction locktime integer 285 | * @param {object?} params.options.rates Miner fee quote 286 | * @param {boolean?} params.options.sort Automatically sort using BIP-69 287 | * @returns {nimble.Transaction} 288 | */ 289 | export function forgeTx(params = {}) { 290 | const forge = new Forge(params) 291 | return forge.toTx() 292 | } 293 | 294 | // Returns the byte size of a TxIn with the given script. 295 | function inputSize(script) { 296 | const buf = new BufferWriter() 297 | writeVarint(buf, script.length) 298 | buf.write(script.buffer) 299 | return 36 + buf.length + 4 300 | } 301 | 302 | // Returns the byte size of a TxOut with the given script. 303 | function outputSize(script) { 304 | const buf = new BufferWriter() 305 | writeVarint(buf, script.length) 306 | buf.write(script.buffer) 307 | return 8 + buf.length 308 | } 309 | 310 | // Returns the byte size of a VarInt of the given integer. 311 | function varintSize(num) { 312 | const buf = new BufferWriter() 313 | writeVarint(buf, num) 314 | return buf.length 315 | } 316 | -------------------------------------------------------------------------------- /src/classes/shared.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Searches the given parameters and returns the first value found from the 3 | * array of keys. Optionally a default value can be set in case none of the keys 4 | * exist. 5 | * 6 | * @param {object} params Parameters object 7 | * @param {string[]} keys List of keys 8 | * @param {any} defaultVal Default value 9 | */ 10 | export function getOpt(params = {}, keys = [], defaultVal) { 11 | for (let i = 0; i < keys.length; i++) { 12 | const key = keys[i], val = params[key] 13 | if (typeof val !== 'undefined') return val; 14 | } 15 | 16 | return defaultVal 17 | } -------------------------------------------------------------------------------- /src/classes/tape.js: -------------------------------------------------------------------------------- 1 | import nimble from '@runonbitcoin/nimble' 2 | 3 | const { BufferWriter, Script } = nimble.classes 4 | const { opcodes } = nimble.constants 5 | const { isBuffer } = nimble.functions 6 | 7 | /** 8 | * The Tape class is a Bitcoin Script builder. Each Cast instance has a Tape 9 | * instance on the `script` property. 10 | * 11 | * Tape provides a simple API for pushing new cells containing Op Codes or data 12 | * vectors onto the tape. Tape functions can be chained together. 13 | * 14 | * The tape is later compiled to a Script instance. 15 | */ 16 | export class Tape { 17 | constructor(cast) { 18 | this.cast = cast ? cast : { script: this } 19 | this.cells = [] 20 | } 21 | 22 | /** 23 | * Applies the macro function with the given array of arguments. The function 24 | * will be invoked with the Cast instance as the `this` context. 25 | * 26 | * ## Example 27 | * 28 | * ``` 29 | * // Define a macro 30 | * function hello(data) { 31 | * this.script.push('hello').push(data) 32 | * } 33 | * 34 | * // Apply the macro 35 | * tape.apply(hello, ['world']) 36 | * ``` 37 | * 38 | * @param {function} macro Macro function 39 | * @param {any[]} args Array of arguments 40 | * @return {Tape} 41 | */ 42 | apply(macro, args = []) { 43 | if (typeof macro !== 'function') { 44 | throw new Error(`invalid argument. callback must be a function.`) 45 | } 46 | if (!Array.isArray(args)) { 47 | throw new Error(`invalid args. must be an array of arguments.`) 48 | } 49 | 50 | macro.apply(this.cast, args) 51 | return this 52 | } 53 | 54 | /** 55 | * Pushes the given data onto the Tape. `data` can be an Op Code, string or 56 | * byte vector, or an array of these types. 57 | * 58 | * To push integers onto the stack, first convert to a Script Num using the 59 | * {@link num `num()` helper}. 60 | * 61 | * @param {any | any[]} data Push data 62 | * @return {Tape} 63 | */ 64 | push(data) { 65 | if (typeof data === 'object' && (Number.isInteger(data.opcode) || isBuffer(data.buf))) { 66 | this.cells.push(data) 67 | return this 68 | } 69 | 70 | if (isBuffer(data)) return this.push({ buf: data }); 71 | 72 | if (Array.isArray(data)) { 73 | data.forEach(part => this.push(part)) 74 | return this 75 | } 76 | 77 | if (data && typeof data.toBuffer === 'function') { 78 | return this.push(data.toBuffer()) 79 | } 80 | 81 | switch(typeof data) { 82 | case 'number': return this.push(opcode(data)); 83 | case 'string': return this.push(string(data)); 84 | } 85 | 86 | throw new Error(`invalid push value. type ${typeof data} not supported.`) 87 | } 88 | 89 | /** 90 | * Iterates over the given elements invoking the callback on each. The 91 | * callback will be invoked with the Cast instance as the `this` context. 92 | * 93 | * ## Example 94 | * 95 | * ``` 96 | * tape.each(['foo', 'bar'], (el, i) => { 97 | * tape.push(el) 98 | * }) 99 | * ``` 100 | * 101 | * @param {any[]} elements Iterable elements 102 | * @param {function} callback Callback function 103 | * @return {Tape} 104 | */ 105 | each(elements, callback) { 106 | if (!Array.isArray(elements)) { 107 | throw new Error(`invalid each. first argument must be an array.`) 108 | } 109 | 110 | for (let i = 0; i < elements.length; i++) this.apply(callback, [elements[i], i]) 111 | return this 112 | } 113 | 114 | /** 115 | * Iterates the given number of times invoking the callback on each loop. The 116 | * callback will be invoked with the Cast instance as the `this` context. 117 | * 118 | * ## Example 119 | * 120 | * ``` 121 | * tape.repeat(15, (i) => { 122 | * tape.push(OP_1) 123 | * tape.push(OP_SPLIT) 124 | * }) 125 | * ``` 126 | * 127 | * @param {any[]} elements Iterable elements 128 | * @param {function} callback Callback function 129 | * @return {Tape} 130 | */ 131 | repeat(n, callback) { 132 | if (!Number.isInteger(n) || n < 1) { 133 | throw new Error(`invalid repeat. first argument must be non-negative integer.`) 134 | } 135 | 136 | for (let i = 0; i < n; i++) this.apply(callback, [i]) 137 | return this 138 | } 139 | } 140 | 141 | /** 142 | * Compiles the given tape and returns a Bitcoin Script instance. 143 | * 144 | * @param {Tape} tape Tape instance 145 | * @returns {nimble.Script} 146 | */ 147 | export function toScript(tape) { 148 | if (!(tape && Array.isArray(tape.cells))) { 149 | throw new Error('invalid argument. `toScript()` must be given a Tape instance.') 150 | } 151 | 152 | const buf = new BufferWriter() 153 | 154 | for (let i = 0; i < tape.cells.length; i++) { 155 | const cell = tape.cells[i] 156 | 157 | if (cell.buf && cell.buf.length) { 158 | if (cell.buf.length < 76) { 159 | buf.write([cell.buf.length]) 160 | buf.write(cell.buf) 161 | } else if (cell.buf.length < 0x100) { 162 | buf.write([76]) 163 | buf.write(uint8(cell.buf.length)) 164 | buf.write(cell.buf) 165 | } else if (cell.buf.length < 0x10000) { 166 | buf.write([77]) 167 | buf.write(uint16(cell.buf.length)) 168 | buf.write(cell.buf) 169 | } else if (cell.buf.length < 0x100000000) { 170 | buf.write([78]) 171 | buf.write(uint32(cell.buf.length)) 172 | buf.write(cell.buf) 173 | } else { 174 | throw new Error('pushdata cannot exceed 4,294,967,296 bytes') 175 | } 176 | } else if (Number.isInteger(cell.opcode)) { 177 | buf.write([cell.opcode]) 178 | } 179 | } 180 | 181 | return new Script(buf.toBuffer()) 182 | } 183 | 184 | // Casts byte as an opcode object 185 | function opcode(byte) { 186 | if (!Number.isInteger(byte) || !Object.values(opcodes).includes(byte)) { 187 | throw new Error('Invalid push value. Must be valid OP CODE byte value.') 188 | } 189 | 190 | return { opcode: byte } 191 | } 192 | 193 | // Casts string as a utf8 buffer 194 | function string(str) { 195 | const enc = new TextEncoder() 196 | return enc.encode(str) 197 | } 198 | 199 | // Casts integer as a Uint8Array 200 | function uint8(num) { 201 | const buf = new ArrayBuffer(1) 202 | const view = new DataView(buf) 203 | view.setUint8(0, num) 204 | return new Uint8Array(view.buffer) 205 | } 206 | 207 | // Casts integer as a Uint16Array 208 | function uint16(num) { 209 | const buf = new ArrayBuffer(2) 210 | const view = new DataView(buf) 211 | view.setUint16(0, num, true) 212 | return new Uint8Array(view.buffer) 213 | } 214 | 215 | // Casts integer as a Uint32Array 216 | function uint32(num) { 217 | const buf = new ArrayBuffer(4) 218 | const view = new DataView(buf) 219 | view.setUint32(0, num, true) 220 | return new Uint8Array(view.buffer) 221 | } 222 | -------------------------------------------------------------------------------- /src/classes/utxo.js: -------------------------------------------------------------------------------- 1 | import nimble from '@runonbitcoin/nimble' 2 | import { getOpt } from './shared.js' 3 | 4 | const { Script, Transaction } = nimble.classes 5 | const { isHex } = nimble.functions 6 | 7 | /** 8 | * A UTXO is an Unspent Transaction Output. 9 | * 10 | * Unlocking Casts spend UTXOs so it is usually required to prepare UTXO 11 | * instances using data from your UTXO database or api. 12 | * 13 | * See {@link toUTXO}. 14 | */ 15 | export class UTXO { 16 | constructor({ txid, vout, satoshis, script } = {}) { 17 | this.txid = txid 18 | this.vout = vout 19 | this.satoshis = satoshis 20 | this.script = script 21 | } 22 | 23 | get output() { 24 | if (this.satoshis && this.script) { 25 | return new Transaction.Output(this.script, this.satoshis) 26 | } 27 | } 28 | } 29 | 30 | /** 31 | * Creates a UTXO from the given parameters. 32 | * 33 | * Will intelligently handle parameters from most UTXO apis. 34 | * 35 | * @param {string|nimble.Script} params.script Previous output script 36 | * @param {string?} params.txid Transaction ID 37 | * @param {string?} params.tx_hash Transaction ID 38 | * @param {number?} params.satoshis Previous output satoshis 39 | * @param {number?} params.value Previous output satoshis 40 | * @param {number?} params.vout Previous output index 41 | * @param {number?} params.outputIndex Previous output index 42 | * @param {number?} params.tx_pos Previous output index 43 | * @returns {UTXO} 44 | */ 45 | export function toUTXO({ script, ...params } = {}) { 46 | const txid = getOpt(params, ['txid', 'tx_hash']) 47 | const vout = getOpt(params, ['vout', 'outputIndex', 'tx_pos']) 48 | const satoshis = getOpt(params, ['satoshis', 'value']) 49 | 50 | if (isHex(script)) { 51 | script = Script.fromHex(script) 52 | } 53 | 54 | return new UTXO({ txid, vout, satoshis, script }) 55 | } 56 | 57 | /** 58 | * Get a UTXO from the given transaction and output index. 59 | * 60 | * @param {nimble.Transaction} tx Transaction 61 | * @param {number} vout Transaction output index 62 | */ 63 | export function getUTXO(tx, vout) { 64 | if (!tx || !tx.outputs[vout]) { 65 | throw new Error(`cannot create utxo from given tx and vout (${ vout })`) 66 | } 67 | 68 | return new UTXO({ 69 | txid: tx.hash, 70 | vout, 71 | satoshis: tx.outputs[vout].satoshis, 72 | script: tx.outputs[vout].script, 73 | }) 74 | } 75 | -------------------------------------------------------------------------------- /src/extra/r-puzzle.js: -------------------------------------------------------------------------------- 1 | import nimble from '@runonbitcoin/nimble' 2 | import { BN_SIZE, PT_SIZE, getMemoryBuffer, getSecp256k1Exports, writeBN, readBN } from '@runonbitcoin/nimble/wasm/wasm-secp256k1.js' 3 | 4 | const { encodeHex, generatePrivateKey } = nimble.functions 5 | 6 | const secp256k1_N = BigInt('0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141') 7 | 8 | /** 9 | * Generates and returns a random K value. 10 | * 11 | * @returns {Uint8Array} 12 | */ 13 | export const generateK = generatePrivateKey 14 | 15 | /** 16 | * Calculates and returns the R value from the give K value. 17 | * 18 | * @param {Uint8Array} k K value 19 | * @returns {Uint8Array} 20 | */ 21 | export function calculateR(k) { 22 | const memory = getMemoryBuffer() 23 | const privateKeyPos = memory.length - BN_SIZE 24 | const publicKeyPos = privateKeyPos - PT_SIZE 25 | 26 | writeBN(memory, privateKeyPos, k) 27 | 28 | getSecp256k1Exports().g_mul(publicKeyPos, privateKeyPos) 29 | 30 | const point = { 31 | x: readBN(memory, publicKeyPos), 32 | y: readBN(memory, publicKeyPos + BN_SIZE) 33 | } 34 | 35 | const rBn = BigInt(`0x${ encodeHex(point.x) }`) % secp256k1_N 36 | const rBuf = bnToBuf(rBn) 37 | 38 | if (rBuf[0] > 127) { 39 | const buf = new Uint8Array(rBuf.length + 1) 40 | buf.set([0], 0) 41 | buf.set(rBuf, 1) 42 | return buf 43 | } else { 44 | return rBuf 45 | } 46 | } 47 | 48 | // Converts js bigint to uint8array 49 | function bnToBuf(bn) { 50 | var hex = BigInt(bn).toString(16); 51 | if (hex.length % 2) { hex = '0' + hex; } 52 | 53 | var len = hex.length / 2; 54 | var u8 = new Uint8Array(len); 55 | 56 | var i = 0; 57 | var j = 0; 58 | while (i < len) { 59 | u8[i] = parseInt(hex.slice(j, j+2), 16); 60 | i += 1; 61 | j += 2; 62 | } 63 | 64 | return u8; 65 | } 66 | -------------------------------------------------------------------------------- /src/helpers/asm.js: -------------------------------------------------------------------------------- 1 | import nimble from '@runonbitcoin/nimble' 2 | 3 | const { decodeASM, decodeScriptChunks } = nimble.functions 4 | 5 | /** 6 | * Decodes the ASM string and returns array of script chunks. 7 | * 8 | * @param {string} str ASM string 9 | * @returns {array} 10 | */ 11 | export function asm(str) { 12 | return decodeScriptChunks(decodeASM(str)) 13 | } 14 | -------------------------------------------------------------------------------- /src/helpers/index.js: -------------------------------------------------------------------------------- 1 | export { asm } from './asm.js' 2 | export { num, scriptNum } from './num.js' -------------------------------------------------------------------------------- /src/helpers/num.js: -------------------------------------------------------------------------------- 1 | import nimble from '@runonbitcoin/nimble' 2 | 3 | const { decodeHex } = nimble.functions 4 | 5 | /** 6 | * If num is 0-16 the appropriate opcode is returned. otherwise a ScriptNum 7 | * buffer is returned. 8 | * 9 | * @param {number} num Number 10 | * @returns {number|number[]} 11 | */ 12 | export function num(num) { 13 | if (num === 0) { 14 | return 0; 15 | } else if (num > 0 && num <= 16) { 16 | return num + 80 17 | } else { 18 | return scriptNum(num) 19 | } 20 | } 21 | 22 | /** 23 | * Encodes the given integer as a ScriptNum byte vector. 24 | * 25 | * @param {number} num Number 26 | * @returns {number[]} 27 | */ 28 | export function scriptNum(num) { 29 | if (num === 0) return [] 30 | const neg = num < 0 31 | const arr = Array.from(decodeHex(BigInt(num).toString(16))).reverse() 32 | const full = arr[arr.length - 1] & 0x80 33 | if (full) arr.push(0x00) 34 | if (neg) arr[arr.length - 1] |= 0x80 35 | return arr 36 | } 37 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import nimble from '@runonbitcoin/nimble' 2 | 3 | export { Forge, createForge, forgeTx } from './classes/forge.js' 4 | export { Cast, isCast } from './classes/cast.js' 5 | export { Tape, toScript } from './classes/tape.js' 6 | export { UTXO, toUTXO, getUTXO } from './classes/utxo.js' 7 | export * as casts from './casts/index.js' 8 | export * as helpers from './helpers/index.js' 9 | export * as macros from './macros/index.js' 10 | 11 | export const deps = { 12 | nimble 13 | } 14 | -------------------------------------------------------------------------------- /src/macros/binary.js: -------------------------------------------------------------------------------- 1 | import nimble from '@runonbitcoin/nimble' 2 | import { num } from '../helpers/index.js' 3 | 4 | const { OP_1, OP_BIN2NUM, OP_CAT, OP_DROP, OP_NIP, OP_SIZE, OP_SPLIT, OP_SUB, OP_SWAP } = nimble.constants.opcodes 5 | 6 | /** 7 | * Decodes the top stack element as a ScriptNum encoded number. 8 | * 9 | * @param {string} endian Endianness (defaults 'le') 10 | * @returns {void} 11 | */ 12 | export function decodeUint(endian = 'le') { 13 | if (['be', 'big'].includes(endian)) { 14 | throw new Error('big endian decoding not implemented yet') 15 | 16 | } else { 17 | this.script 18 | .push([0]) 19 | .push(OP_CAT) 20 | .push(OP_BIN2NUM) 21 | } 22 | } 23 | 24 | /** 25 | * Reverses the top item on the stack. 26 | * 27 | * This macro pushes op codes on to the script that will reverse a binary of the 28 | * given length. 29 | * 30 | * @param {number} len Length in bytes 31 | * @returns {void} 32 | */ 33 | export function reverse(len) { 34 | for (let i = 1; i < len; i++) { 35 | this.script.push(OP_1).push(OP_SPLIT) 36 | } 37 | 38 | for (let i = 1; i < len; i++) { 39 | this.script.push(OP_SWAP).push(OP_CAT) 40 | } 41 | } 42 | 43 | /** 44 | * Slices bytes from top item on the stack, starting on the given `start` index 45 | * for `length` bytes. The stack item is replaced with the sliced value. 46 | * 47 | * Byte vectors are zero indexed. If `start` is a negative integer, then the 48 | * start index is counted from the end. 49 | * 50 | * @param {number} start Start index 51 | * @param {number} length Bytes to slice 52 | * @returns {void} 53 | */ 54 | export function slice(start, length) { 55 | if (start === 0) { 56 | this.script 57 | .push(num(length)) 58 | .push(OP_SPLIT) 59 | .push(OP_DROP) 60 | 61 | } else if (start > 0) { 62 | this.script 63 | .apply(trim, [start]) 64 | .apply(slice, [0, length]) 65 | 66 | } else if (start < 0) { 67 | this.script 68 | .push(OP_SIZE) 69 | .push(num(start * -1)) 70 | .push(OP_SUB) 71 | .push(OP_SPLIT) 72 | .push(OP_NIP) 73 | .apply(slice, [0, length]) 74 | } 75 | } 76 | 77 | /** 78 | * Trims the given number of leading or trailing bytes from the top item on the 79 | * stack. The stack item is replaced with the trimmed value. 80 | * 81 | * When the given `length` is a positive integer, leading bytes are trimmed. 82 | * When a negative integer is given, trailing bytes are trimmed. 83 | * 84 | * @param {number} length Bytes to trim 85 | * @returns {void} 86 | */ 87 | export function trim(length) { 88 | if (length > 0) { 89 | this.script 90 | .push(num(length)) 91 | .push(OP_SPLIT) 92 | .push(OP_NIP) 93 | 94 | } else if (length < 0) { 95 | this.script 96 | .push(OP_SIZE) 97 | .push(num(length * -1)) 98 | .push(OP_SUB) 99 | .push(OP_SPLIT) 100 | .push(OP_DROP) 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/macros/index.js: -------------------------------------------------------------------------------- 1 | export { decodeUint, reverse, slice, trim } from './binary.js' 2 | export { 3 | getVersion, getPrevoutsHash, getSequenceHash, getOutpoint, 4 | getScript, getSatoshis, getSequence, getOutputsHash, 5 | getLocktime, getSighashType, 6 | pushTx, checkTx, checkTxVerify, checkTxOpt, checkTxOptVerify 7 | } from './push-tx.js' 8 | export { signTx, signTxWithK } from './sign-tx.js' 9 | export { getVarint, readVarint, trimVarint } from './varint.js' 10 | -------------------------------------------------------------------------------- /src/macros/push-tx.js: -------------------------------------------------------------------------------- 1 | import nimble from '@runonbitcoin/nimble' 2 | import { asm } from '../helpers/index.js' 3 | import { decodeUint, reverse, slice, trim } from './binary.js' 4 | import { trimVarint } from './varint.js' 5 | 6 | const { 7 | OP_CAT, OP_DUP, OP_HASH256, OP_SPLIT, OP_SWAP, OP_TUCK, 8 | OP_IF, OP_ELSE, OP_ENDIF, OP_CHECKSIG, OP_CHECKSIGVERIFY 9 | } = nimble.constants.opcodes 10 | const { decodeHex, preimage } = nimble.functions 11 | 12 | const ORDER_PREFIX = decodeHex('414136d08c5ed2bf3ba048afe6dcaebafe') 13 | const PUBKEY_A = decodeHex('023635954789a02e39fb7e54440b6f528d53efd65635ddad7f3c4085f97fdbdc48') 14 | const PUBKEY_B = decodeHex('038ff83d8cf12121491609c4939dc11c4aa35503508fe432dc5a5c1905608b9218') 15 | const PUBKEY_OPT = decodeHex('02b405d7f0322a89d0f9f3a98e6f938fdc1c969a8d1382a2bf66a71ae74a1e83b0') 16 | const SIG_PREFIX = decodeHex('3044022079be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f817980220') 17 | const SIGHASH_FLAG = 0x41 18 | 19 | /** 20 | * Assuming the top stack item is a Tx Preimage, gets the tx version number and 21 | * places it on the stack on top of the preimage. 22 | * 23 | * @returns {void} 24 | */ 25 | export function getVersion() { 26 | this.script 27 | .push(OP_DUP) 28 | .apply(slice, [0, 4]) 29 | .apply(decodeUint) 30 | } 31 | 32 | /** 33 | * Assuming the top stack item is a Tx Preimage, gets the 32 byte prevouts hash 34 | * and places it on the stack on top of the preimage. 35 | * 36 | * @returns {void} 37 | */ 38 | export function getPrevoutsHash() { 39 | this.script 40 | .push(OP_DUP) 41 | .apply(slice, [4, 32]) 42 | } 43 | 44 | /** 45 | * Assuming the top stack item is a Tx Preimage, gets the 32 byte sequence hash 46 | * and places it on the stack on top of the preimage. 47 | * 48 | * @returns {void} 49 | */ 50 | export function getSequenceHash() { 51 | this.script 52 | .push(OP_DUP) 53 | .apply(slice, [36, 32]) 54 | } 55 | 56 | /** 57 | * Assuming the top stack item is a Tx Preimage, gets the 36 byte outpoint and 58 | * places it on the stack on top of the preimage. 59 | * 60 | * @returns {void} 61 | */ 62 | export function getOutpoint() { 63 | this.script 64 | .push(OP_DUP) 65 | .apply(slice, [68, 36]) 66 | } 67 | 68 | /** 69 | * Assuming the top stack item is a Tx Preimage, gets the locking script and 70 | * places it on the stack on top of the preimage. 71 | * 72 | * State can be placed in the locking script and so this becomes an invaluable 73 | * method for extracting and using that state. 74 | * 75 | * @returns {void} 76 | */ 77 | export function getScript() { 78 | this.script 79 | .push(OP_DUP) 80 | .apply(trim, [104]) 81 | .apply(trim, [-52]) 82 | .apply(trimVarint) 83 | } 84 | 85 | /** 86 | * Assuming the top stack item is a Tx Preimage, gets the input satoshis number 87 | * and places it on the stack on top of the preimage. 88 | * 89 | * @returns {void} 90 | */ 91 | export function getSatoshis() { 92 | this.script 93 | .push(OP_DUP) 94 | .apply(slice, [-52, 8]) 95 | .apply(decodeUint) 96 | } 97 | 98 | /** 99 | * Assuming the top stack item is a Tx Preimage, gets the input sequence number 100 | * and places it on the stack on top of the preimage. 101 | * 102 | * @returns {void} 103 | */ 104 | export function getSequence() { 105 | this.script 106 | .push(OP_DUP) 107 | .apply(slice, [-44, 4]) 108 | .apply(decodeUint) 109 | } 110 | 111 | /** 112 | * Assuming the top stack item is a Tx Preimage, gets the 32 byte outputs hash 113 | * and places it on the stack on top of the preimage. 114 | * 115 | * @returns {void} 116 | */ 117 | export function getOutputsHash() { 118 | this.script 119 | .push(OP_DUP) 120 | .apply(slice, [-40, 32]) 121 | } 122 | 123 | /** 124 | * Assuming the top stack item is a Tx Preimage, gets the tx locktime number and 125 | * places it on the stack on top of the preimage. 126 | * 127 | * @returns {void} 128 | */ 129 | export function getLocktime() { 130 | this.script 131 | .push(OP_DUP) 132 | .apply(slice, [-8, 4]) 133 | .apply(decodeUint) 134 | } 135 | 136 | /** 137 | * Assuming the top stack item is a Tx Preimage, gets the preimage sighash type 138 | * and places it on the stack on top of the preimage. 139 | * 140 | * @returns {void} 141 | */ 142 | export function getSighashType() { 143 | this.script 144 | .push(OP_DUP) 145 | .apply(slice, [-4, 4]) 146 | .apply(decodeUint) 147 | } 148 | 149 | /** 150 | * Pushes the current Tx Preimage onto the stack. If no tx context is available, 151 | * then 181 bytes of zeros are pushed onto the script instead. 152 | * 153 | * @returns {void} 154 | */ 155 | export function pushTx() { 156 | const preimg = this.ctx ? 157 | preimage( 158 | this.ctx.tx, 159 | this.ctx.vin, 160 | this.utxo.script, 161 | this.utxo.satoshis, 162 | SIGHASH_FLAG 163 | ) : 164 | new Uint8Array(181); 165 | 166 | this.script.push(preimg) 167 | } 168 | 169 | /** 170 | * Assuming the top stack item is a Tx Preimage, creates and verifies a 171 | * signature with `OP_CHECKSIG`. 172 | * 173 | * The Tx Preimage is removed from the stack and replaced with the result from 174 | * `OP_CHECKSIG`. 175 | * 176 | * @returns {void} 177 | */ 178 | export function checkTx() { 179 | this.script 180 | .push(OP_HASH256) 181 | .apply(prepareSighash) 182 | .apply(pushOrder) 183 | .apply(divOrder) 184 | .apply(sighashMSBis0or255) 185 | .push(asm('OP_IF OP_2 OP_PICK OP_ADD OP_ELSE OP_1ADD OP_ENDIF')) 186 | .apply(sighashModGtOrder) 187 | .push(asm('OP_IF OP_SUB OP_ELSE OP_NIP OP_ENDIF')) 188 | .apply(pushSig) 189 | .push(OP_SWAP) 190 | .push(OP_IF) 191 | .push(PUBKEY_A) 192 | .push(OP_ELSE) 193 | .push(PUBKEY_B) 194 | .push(OP_ENDIF) 195 | .push(OP_CHECKSIG) 196 | } 197 | 198 | /** 199 | * As {@link checkTx} but verifies the signature with `OP_CHECKSIGVERIFY`. 200 | * 201 | * @returns {void} 202 | */ 203 | export function checkTxVerify() { 204 | this.script.apply(checkTx) 205 | this.script.cells.pop() 206 | this.script.push(OP_CHECKSIGVERIFY) 207 | } 208 | 209 | // Prepares the sighash and MSB 210 | function prepareSighash() { 211 | this.script 212 | .apply(reverse, [32]) 213 | .push([0x1F]) 214 | .push(OP_SPLIT) 215 | .push(OP_TUCK) 216 | .push(OP_CAT) 217 | .apply(decodeUint) 218 | } 219 | 220 | // Pushes the secp256k1 order onto the stack 221 | function pushOrder() { 222 | this.script 223 | .push(ORDER_PREFIX) 224 | .push(asm('00 OP_15 OP_NUM2BIN OP_INVERT OP_CAT 00 OP_CAT')) 225 | } 226 | 227 | // Divides the order by 2 228 | function divOrder() { 229 | this.script.push(asm('OP_DUP OP_2 OP_DIV')) 230 | } 231 | 232 | // Is the sighash MSB 0x00 or 0xFF 233 | function sighashMSBis0or255() { 234 | this.script 235 | .push(asm('OP_ROT OP_3 OP_ROLL OP_DUP ff OP_EQUAL OP_SWAP 00 OP_EQUAL OP_BOOLOR OP_TUCK')) 236 | } 237 | 238 | // Is the sighash mod greater than the secp256k1 order 239 | function sighashModGtOrder() { 240 | this.script.push( 241 | asm('OP_3 OP_ROLL OP_TUCK OP_MOD OP_DUP OP_4 OP_ROLL OP_GREATERTHAN') 242 | ) 243 | } 244 | 245 | // Constructs and pushes the signature onto the stack 246 | function pushSig() { 247 | this.script 248 | .push(SIG_PREFIX) 249 | .push(OP_SWAP) 250 | .apply(reverse, [32]) 251 | .push(OP_CAT) 252 | .push([SIGHASH_FLAG]) 253 | .push(OP_CAT) 254 | } 255 | 256 | /** 257 | * Assuming the top stack item is a Tx Preimage, creates and verifies a 258 | * signature with `OP_CHECKSIG`. 259 | * 260 | * This uses the {@link https://xiaohuiliu.medium.com/optimal-op-push-tx-ded54990c76f optimal OP_PUSH_TX approach} 261 | * which compiles to 87 bytes (compared to 438 as per {@link checkTx}). 262 | * 263 | * However, due to the {@link https://bitcoin.stackexchange.com/questions/85946/low-s-value-in-bitcoin-signature Low-S Constraint} 264 | * the most significant byte of the sighash must be less than a theshold of 265 | * `0x7E`. There is a roughly 50% chance the signature being invalid. Therefore, 266 | * when using this technique it is necessary to check the preimage and if 267 | * necessary continue to malleate the transaction until it is valid. 268 | * 269 | * @returns {void} 270 | */ 271 | export function checkTxOpt() { 272 | this.script 273 | .push(OP_HASH256) 274 | .apply(add1ToHash) 275 | .apply(pushSigOpt) 276 | .push(PUBKEY_OPT) 277 | .push(OP_CHECKSIG) 278 | } 279 | 280 | /** 281 | * As {@link checkTxOpt} but verifies the signature with `OP_CHECKSIGVERIFY`. 282 | * 283 | * @returns {void} 284 | */ 285 | export function checkTxOptVerify() { 286 | this.script.apply(checkTxOpt) 287 | this.script.cells.pop() 288 | this.script.push(OP_CHECKSIGVERIFY) 289 | } 290 | 291 | // Adds 1 to the sighash MSB 292 | function add1ToHash() { 293 | this.script.push( 294 | asm('OP_1 OP_SPLIT OP_SWAP OP_BIN2NUM OP_1ADD OP_SWAP OP_CAT') 295 | ) 296 | } 297 | 298 | // Constructs and pushes the signature onto the stack (optimal version) 299 | function pushSigOpt() { 300 | this.script 301 | .push(SIG_PREFIX) 302 | .push(OP_SWAP) 303 | .push(OP_CAT) 304 | .push([SIGHASH_FLAG]) 305 | .push(OP_CAT) 306 | } 307 | -------------------------------------------------------------------------------- /src/macros/sign-tx.js: -------------------------------------------------------------------------------- 1 | import nimble from '@runonbitcoin/nimble' 2 | 3 | const { sighashFlags } = nimble.constants 4 | const { generateTxSignature } = nimble.functions 5 | const { ecdsaSignWithK, encodeDER, sighash } = nimble.functions 6 | 7 | /** 8 | * Signs the transaction context and pushes the signature onto the script. 9 | * 10 | * If no tx context is available then 71 bytes of zeros are pushed onto the 11 | * script instead. 12 | * 13 | * @param {nimble.PrivateKey} privkey Private key 14 | * @returns {void} 15 | */ 16 | export function signTx(privkey) { 17 | // If ctx, create signature, otherwise empty 71 bytes 18 | const sig = this.ctx ? 19 | generateTxSignature( 20 | this.ctx.tx, 21 | this.ctx.vin, 22 | this.utxo.script, 23 | this.utxo.satoshis, 24 | privkey.number, 25 | privkey.toPublicKey().point, 26 | this.opts.sighashFlags || sighashFlags.SIGHASH_ALL 27 | ) : 28 | new Uint8Array(71); 29 | 30 | this.script.push(sig) 31 | } 32 | 33 | /** 34 | * As {@link signTx} but can be passed a K which is used in the signature 35 | * generation. 36 | * 37 | * @param {nimble.PrivateKey} privkey Private key 38 | * @param {Uint8Array} k K value 39 | * @returns {void} 40 | */ 41 | export function signTxWithK(privkey, k) { 42 | // If ctx, create signature, otherwise empty 71 bytes 43 | const sig = this.ctx ? 44 | generateTxSigWithK( 45 | this.ctx.tx, 46 | this.ctx.vin, 47 | this.utxo.script, 48 | this.utxo.satoshis, 49 | privkey, 50 | k, 51 | this.opts.sighashFlags || sighashFlags.SIGHASH_ALL 52 | ) : 53 | new Uint8Array(71); 54 | 55 | this.script.push(sig) 56 | } 57 | 58 | // Generates the tx signature with custom K value 59 | function generateTxSigWithK(tx, vin, script, satoshis, privkey, k, flags) { 60 | const hash = sighash(tx, vin, script, satoshis, flags) 61 | const sig = ecdsaSignWithK(hash, k, privkey.number, privkey.toPublicKey().point) 62 | return Array.from([...encodeDER(sig), flags]) 63 | } 64 | -------------------------------------------------------------------------------- /src/macros/varint.js: -------------------------------------------------------------------------------- 1 | import nimble from '@runonbitcoin/nimble' 2 | import { decodeUint, trim } from './binary.js' 3 | import { num } from '../helpers/index.js' 4 | 5 | const { 6 | OP_1, 7 | OP_DROP, 8 | OP_DUP, 9 | OP_ELSE, 10 | OP_ENDIF, 11 | OP_EQUAL, 12 | OP_IF, 13 | OP_NIP, 14 | OP_SPLIT, 15 | OP_SWAP 16 | } = nimble.constants.opcodes 17 | 18 | /** 19 | * Assuming the top stack item is a VarInt encoded byte vector, the VarInt is 20 | * copied and placed on top of the stack as a ScriptNum. 21 | * 22 | * The original element is not removed. 23 | * 24 | * Use this function if you would like to to extract the VarInt number, yet 25 | * leave the original data on the stack. 26 | * 27 | * @returns {void} 28 | */ 29 | export function getVarint() { 30 | this.script 31 | .push(OP_DUP) 32 | .apply(varintSwitch, [doGetVarint]) 33 | } 34 | 35 | // Extract and decode the VarInt number 36 | function doGetVarint(bytes) { 37 | if (bytes === 1) { 38 | this.script.push(OP_NIP) 39 | 40 | } else { 41 | this.script 42 | .push(OP_DROP) 43 | .push(num(bytes)) 44 | .push(OP_SPLIT) 45 | .push(OP_DROP) 46 | } 47 | 48 | this.script.apply(decodeUint) 49 | } 50 | 51 | /** 52 | * Assuming the top stack item is a VarInt encoded byte vector, the VarInt 53 | * is extracted and placed on top of the stack as a ScriptNum. 54 | * 55 | * The original element is removed and any remaining data is second on the 56 | * stack. 57 | * 58 | * Use this function if the VarInt is part of a larger string of bytes and you 59 | * would like to extract the data whilst retaining the remaining bytes. 60 | * 61 | * @returns {void} 62 | */ 63 | export function readVarint() { 64 | this.script.apply(varintSwitch, [doReadVarint]) 65 | } 66 | 67 | // Extract the VarInt data and place on top 68 | function doReadVarint(bytes) { 69 | if (bytes > 1) { 70 | this.script 71 | .push(OP_DROP) 72 | .push(num(bytes)) 73 | .push(OP_SPLIT) 74 | .push(OP_SWAP) 75 | } 76 | 77 | this.script 78 | .apply(decodeUint) 79 | .push(OP_SPLIT) 80 | .push(OP_SWAP) 81 | } 82 | 83 | /** 84 | * Assuming the top stack item is a VarInt encoded binary, the VarInt prefix 85 | * is trimmed from the leading bytes and the encoded data is placed on top of 86 | * the stack. 87 | * 88 | * The original element is removed. 89 | * 90 | * Use this function if you would like to cleanly trim the VarInt number from 91 | * the encoded data. 92 | * 93 | * @returns {void} 94 | */ 95 | export function trimVarint() { 96 | this.script.apply(varintSwitch, [doTrimVarint]) 97 | } 98 | 99 | // Trim varint from leading bytes 100 | function doTrimVarint(bytes) { 101 | this.script.push(OP_DROP) 102 | 103 | if (bytes > 1) { 104 | this.script.apply(trim, [bytes]) 105 | } 106 | } 107 | 108 | // Shared VarInt switch statement 109 | function varintSwitch(handleVarint) { 110 | this.script 111 | .push(OP_1) 112 | .push(OP_SPLIT) 113 | .push(OP_SWAP) 114 | .push(OP_DUP) 115 | .push([253]) 116 | .push(OP_EQUAL) 117 | .push(OP_IF) 118 | .apply(handleVarint, [2]) 119 | .push(OP_ELSE) 120 | .push(OP_DUP) 121 | .push([254]) 122 | .push(OP_EQUAL) 123 | .push(OP_IF) 124 | .apply(handleVarint, [4]) 125 | .push(OP_ELSE) 126 | .push(OP_DUP) 127 | .push([255]) 128 | .push(OP_EQUAL) 129 | .push(OP_IF) 130 | .apply(handleVarint, [8]) 131 | .push(OP_ELSE) 132 | .apply(handleVarint, [1]) 133 | .push(OP_ENDIF) 134 | .push(OP_ENDIF) 135 | .push(OP_ENDIF) 136 | } 137 | -------------------------------------------------------------------------------- /test/casts/opreturn.test.js: -------------------------------------------------------------------------------- 1 | import test from 'ava' 2 | import { casts } from '../../src/index.js' 3 | 4 | const { OpReturn } = casts 5 | 6 | test('OpReturn.lock() takes a single data binary parameter', t => { 7 | const cast = OpReturn.lock(0, { data: 'hello world' }) 8 | const output = cast.toOutput() 9 | const script = cast.toScript() 10 | 11 | t.is(output.satoshis, 0) 12 | t.deepEqual(output.script, script) 13 | t.regex(script.toASM(), /^0 OP_RETURN [0-9a-f]{22}$/) 14 | }) 15 | 16 | test('OpReturn.lock() takes a list of data binary parameters', t => { 17 | const cast = OpReturn.lock(0, { data: ['hello', 'world'] }) 18 | const script = cast.toScript() 19 | 20 | t.regex(script.toASM(), /^0 OP_RETURN [0-9a-f]{10} [0-9a-f]{10}$/) 21 | }) 22 | 23 | test('OpReturn.lock() takes a mixed list of parameters', t => { 24 | const cast = OpReturn.lock(0, { data: ['hello', 'world', { opcode: 90 }, [0,1,2,3]] }) 25 | const script = cast.toScript() 26 | 27 | t.regex(script.toASM(), /^0 OP_RETURN [0-9a-f]{10} [0-9a-f]{10} OP_10 00010203$/) 28 | }) 29 | -------------------------------------------------------------------------------- /test/casts/p2ms.test.js: -------------------------------------------------------------------------------- 1 | import test from 'ava' 2 | import nimble from '@runonbitcoin/nimble' 3 | import { casts, toUTXO } from '../../src/index.js' 4 | 5 | const { P2MS } = casts 6 | const keys = new Array(3).fill(0).map(_ => nimble.PrivateKey.fromRandom()) 7 | const pubkeys = keys.map(k => k.toPublicKey()) 8 | const privkeys = keys.slice(0, 2) 9 | const utxo = toUTXO({ 10 | txid: '0000000000000000000000000000000000000000000000000000000000000000', 11 | vout: 0 12 | }) 13 | 14 | test('P2MS.lock() locks satoshis to a threshold of pubkeys', t => { 15 | const cast = P2MS.lock(1000, { pubkeys, threshold: 2 }) 16 | const output = cast.toOutput() 17 | const script = cast.toScript() 18 | 19 | t.is(output.satoshis, 1000) 20 | t.deepEqual(output.script, script) 21 | t.regex(script.toASM(), /^OP_2 ([0-9a-f]{66} ?){3} OP_3 OP_CHECKMULTISIG$/) 22 | }) 23 | 24 | test('P2MS.lock() throws if arguments invalid', t => { 25 | t.throws(() => P2MS.lock(1000, {})) 26 | t.throws(() => P2MS.lock(1000, { pubkeys: ['Not a pubkey'], threshold: 1 })) 27 | t.throws(() => P2MS.lock(1000, { pubkeys, threshold: 'xyz' })) 28 | }) 29 | 30 | test('P2MS.unlock() unlocks UTXO with privkeys', t => { 31 | const cast = P2MS.unlock(utxo, { privkeys }) 32 | const input = cast.toInput() 33 | const script = cast.toScript() 34 | 35 | t.is(input.txid, utxo.txid) 36 | t.is(input.vout, utxo.vout) 37 | t.deepEqual(input.script, script) 38 | t.regex(script.toASM(), /^0 (0{142} ?){2}$/) 39 | }) 40 | 41 | test('P2MS.unlock() throws if arguments invalid', t => { 42 | t.throws(() => P2MS.unlock(utxo, {})) 43 | t.throws(() => P2MS.unlock(utxo, { privkeys: ['a', 'b']})) 44 | }) 45 | 46 | test('P2MS.simulate() evaluates as valid if signed with threshold of keys', t => { 47 | const vm = P2MS.simulate({ pubkeys, threshold: 2 }, { privkeys }) 48 | t.true(vm.success) 49 | }) 50 | 51 | test('P2MS.simulate() evaluates as invalid if signed with insufficient threshold of keys', t => { 52 | const vm = P2MS.simulate({ pubkeys, threshold: 2 }, { privkeys: [privkeys[0]] }) 53 | t.false(vm.success) 54 | t.truthy(vm.error) 55 | }) 56 | 57 | test('P2MS.simulate() evaluates as invalid if signed with incorrect of keys', t => { 58 | const wrongKeys = new Array(2).fill(0).map(_ => nimble.PrivateKey.fromRandom()) 59 | const vm = P2MS.simulate({ pubkeys, threshold: 2 }, { privkeys: wrongKeys }) 60 | t.false(vm.success) 61 | t.truthy(vm.error) 62 | }) 63 | -------------------------------------------------------------------------------- /test/casts/p2pk.test.js: -------------------------------------------------------------------------------- 1 | import test from 'ava' 2 | import nimble from '@runonbitcoin/nimble' 3 | import { casts, toUTXO } from '../../src/index.js' 4 | 5 | const { P2PK } = casts 6 | const privkey = nimble.PrivateKey.fromRandom() 7 | const pubkey = privkey.toPublicKey() 8 | const utxo = toUTXO({ 9 | txid: '0000000000000000000000000000000000000000000000000000000000000000', 10 | vout: 0 11 | }) 12 | 13 | test('P2PK.lock() locks satoshis to a pubkey', t => { 14 | const cast = P2PK.lock(1000, { pubkey }) 15 | const output = cast.toOutput() 16 | const script = cast.toScript() 17 | 18 | t.is(output.satoshis, 1000) 19 | t.deepEqual(output.script, script) 20 | t.regex(script.toASM(), /^[0-9a-f]{66} OP_CHECKSIG$/) 21 | }) 22 | 23 | test('P2PK.lock() throws if arguments invalid', t => { 24 | t.throws(() => P2PK.lock(1000, {})) 25 | t.throws(() => P2PK.lock(1000, { pubkey: 'Not a pubkey' })) 26 | }) 27 | 28 | test('P2PK.unlock() unlocks UTXO with given privkey', t => { 29 | const cast = P2PK.unlock(utxo, { privkey }) 30 | const input = cast.toInput() 31 | const script = cast.toScript() 32 | 33 | t.is(input.txid, utxo.txid) 34 | t.is(input.vout, utxo.vout) 35 | t.deepEqual(input.script, script) 36 | t.regex(script.toASM(), /^0{142}$/) 37 | }) 38 | 39 | test('P2PK.unlock() throws if arguments invalid', t => { 40 | t.throws(() => P2PK.unlock(utxo, {})) 41 | t.throws(() => P2PK.unlock(utxo, { privkey: 'Not a privkey' })) 42 | }) 43 | 44 | test('P2PK.simulate() evaluates as valid if signed with correct key', t => { 45 | const vm = P2PK.simulate({ pubkey }, { privkey }) 46 | t.true(vm.success) 47 | }) 48 | 49 | test('P2PK.simulate() evaluates as invalid if signed with incorrect key', t => { 50 | const wrongKey = nimble.PrivateKey.fromRandom() 51 | const vm = P2PK.simulate({ pubkey }, { privkey: wrongKey }) 52 | t.false(vm.success) 53 | t.truthy(vm.error) 54 | }) 55 | -------------------------------------------------------------------------------- /test/casts/p2pkh.test.js: -------------------------------------------------------------------------------- 1 | import test from 'ava' 2 | import nimble from '@runonbitcoin/nimble' 3 | import { casts, toUTXO } from '../../src/index.js' 4 | 5 | const { P2PKH } = casts 6 | const privkey = nimble.PrivateKey.fromRandom() 7 | const address = privkey.toPublicKey().toAddress() 8 | const utxo = toUTXO({ 9 | txid: '0000000000000000000000000000000000000000000000000000000000000000', 10 | vout: 0 11 | }) 12 | 13 | test('P2PKH.lock() locks satoshis to an address', t => { 14 | const cast = P2PKH.lock(1000, { address }) 15 | const output = cast.toOutput() 16 | const script = cast.toScript() 17 | 18 | t.is(output.satoshis, 1000) 19 | t.deepEqual(output.script, script) 20 | t.regex(script.toASM(), /^OP_DUP OP_HASH160 [0-9a-f]{40} OP_EQUALVERIFY OP_CHECKSIG$/) 21 | }) 22 | 23 | test('P2PKH.lock() accepts address as string', t => { 24 | t.notThrows(() => P2PKH.lock(1000, { address: address.toString() })) 25 | }) 26 | 27 | test('P2PKH.lock() throws if arguments invalid', t => { 28 | t.throws(() => P2PKH.lock(1000, {})) 29 | t.throws(() => P2PKH.lock(1000, { address: 'Not an address' })) 30 | }) 31 | 32 | test('P2PKH.unlock() unlocks UTXO with a privkey', t => { 33 | const cast = P2PKH.unlock(utxo, { privkey }) 34 | const input = cast.toInput() 35 | const script = cast.toScript() 36 | 37 | t.is(input.txid, utxo.txid) 38 | t.is(input.vout, utxo.vout) 39 | t.deepEqual(input.script, script) 40 | t.regex(script.toASM(), /^0{142} [0-9a-f]{66}$/) 41 | }) 42 | 43 | test('P2PKH.unlock() throws if arguments invalid', t => { 44 | t.throws(() => P2PKH.unlock(utxo, {})) 45 | t.throws(() => P2PKH.unlock(utxo, { privkey: 'Not a privkey' })) 46 | }) 47 | 48 | test('P2PKH.simulate() evaluates as valid if signed with correct key', t => { 49 | const vm = P2PKH.simulate({ address }, { privkey }) 50 | t.true(vm.success) 51 | }) 52 | 53 | test('P2PKH.simulate() throws when signed with incorrect key', t => { 54 | const wrongKey = nimble.PrivateKey.fromRandom() 55 | const vm = P2PKH.simulate({ address }, { privkey: wrongKey }) 56 | t.false(vm.success) 57 | t.truthy(vm.error) 58 | }) 59 | -------------------------------------------------------------------------------- /test/casts/p2rph.test.js: -------------------------------------------------------------------------------- 1 | import test from 'ava' 2 | import nimble from '@runonbitcoin/nimble' 3 | import { casts, toUTXO } from '../../src/index.js' 4 | import { generateK, calculateR } from '../../src/extra/r-puzzle.js' 5 | 6 | const { decodeHex, encodeHex, isBuffer } = nimble.functions 7 | const { P2RPH } = casts 8 | const k = generateK() 9 | const r = calculateR(k) 10 | const privkey = nimble.PrivateKey.fromRandom() 11 | const utxo = toUTXO({ 12 | txid: '0000000000000000000000000000000000000000000000000000000000000000', 13 | vout: 0 14 | }) 15 | 16 | test('generateK() returns random 32 bytes', t => { 17 | const k = generateK() 18 | t.true(isBuffer(k)) 19 | t.is(k.length, 32) 20 | }) 21 | 22 | test('calculateR() returns R from K', t => { 23 | // Example taken from elixir tests 24 | const myK = '5c5fe3d59de129819770b3d91a6a5422f8cbb7c28f27a532de1efbc7c4ffb5d6' 25 | const myR = '00906367a7db162403eda7181c5270e9d86beee89d0886a1fe44238fcfbe9cabd3' 26 | 27 | const r = calculateR(decodeHex(myK)) 28 | t.true(isBuffer(r)) 29 | t.is(encodeHex(r), myR) 30 | }) 31 | 32 | test('P2RPH.lock() locks satoshis to an R value', t => { 33 | const cast = P2RPH.lock(1000, { r }) 34 | const output = cast.toOutput() 35 | const script = cast.toScript() 36 | 37 | t.is(output.satoshis, 1000) 38 | t.deepEqual(output.script, script) 39 | t.regex(script.toASM(), /^OP_OVER OP_3 OP_SPLIT OP_NIP OP_1 OP_SPLIT OP_SWAP OP_SPLIT OP_DROP OP_HASH160 [0-9a-f]{40} OP_EQUALVERIFY OP_TUCK OP_CHECKSIGVERIFY OP_CHECKSIG$/) 40 | }) 41 | 42 | test('P2RPH.lock() throws if arguments invalid', t => { 43 | t.throws(() => P2RPH.lock(1000, {})) 44 | t.throws(() => P2RPH.lock(1000, { r: 'Not a buffer' })) 45 | t.throws(() => P2RPH.lock(1000, { r: [0, 1, 2, 3, 4] })) 46 | }) 47 | 48 | test('P2RPH.unlock() unlocks UTXO with given K value', t => { 49 | const cast = P2RPH.unlock(utxo, { k, privkey }) 50 | const input = cast.toInput() 51 | const script = cast.toScript() 52 | 53 | t.is(input.txid, utxo.txid) 54 | t.is(input.vout, utxo.vout) 55 | t.deepEqual(input.script, script) 56 | t.regex(script.toASM(), /^(0{142} ?){2} [0-9a-f]{66}$/) 57 | }) 58 | 59 | test('P2RPH.unlock() signs with random k if none given', t => { 60 | const cast = P2RPH.unlock(utxo, { k }) 61 | const script = cast.toScript() 62 | t.regex(script.toASM(), /^(0{142} ?){2} [0-9a-f]{66}$/) 63 | }) 64 | 65 | test('P2RPH.unlock() throws if arguments invalid', t => { 66 | t.throws(() => P2RPH.unlock(utxo, {})) 67 | t.throws(() => P2RPH.unlock(utxo, { k: 'Not a buffer' })) 68 | t.throws(() => P2RPH.unlock(utxo, { k: [0, 1, 2, 3, 4] })) 69 | }) 70 | 71 | test('P2RPH.simulate() evaluates as valid with correct K', t => { 72 | const vm1 = P2RPH.simulate({ r }, { k, privkey }) 73 | const vm2 = P2RPH.simulate({ r }, { k }) 74 | t.true(vm1.success) 75 | t.true(vm2.success) 76 | }) 77 | 78 | test('P2RPH.simulate() evaluates as invalid with incorrect K', t => { 79 | const wrongK = generateK() 80 | const vm = P2RPH.simulate({ r }, { k: wrongK }) 81 | t.false(vm.success) 82 | t.truthy(vm.error) 83 | }) 84 | -------------------------------------------------------------------------------- /test/casts/raw.test.js: -------------------------------------------------------------------------------- 1 | import test from 'ava' 2 | import nimble from '@runonbitcoin/nimble' 3 | import { casts, toUTXO } from '../../src/index.js' 4 | 5 | const { Raw } = casts 6 | const lockScript = nimble.Script.fromHex('76a914a58621fd80c3abcf4f81d343f6a78dc891082d6688ac') 7 | const unlockScript = nimble.Script.fromHex('47304402205832ed8192e83d640824c021107024f9f03adf1fcc9bca826025ab1ce9f012b9022005d71b00d40023a6bb8a2644f7f599bf367850a1ec5ddc95882c9841a1867c6a412102554669f32b842ec626175d3b5380a335213506d55aa896f04d66d9f46f48cf18') 8 | const utxo = toUTXO({ 9 | txid: '0000000000000000000000000000000000000000000000000000000000000000', 10 | vout: 0 11 | }) 12 | 13 | test('Raw.lock() locks satoshis to an script', t => { 14 | const cast = Raw.lock(1000, { script: lockScript }) 15 | const output = cast.toOutput() 16 | const script = cast.toScript() 17 | 18 | t.is(output.satoshis, 1000) 19 | t.deepEqual(output.script, script) 20 | t.regex(script.toASM(), /^OP_DUP OP_HASH160 [0-9a-f]{40} OP_EQUALVERIFY OP_CHECKSIG$/) 21 | }) 22 | 23 | test('Raw.lock() accepts script as string', t => { 24 | t.notThrows(() => Raw.lock(1000, { script: lockScript.toHex() })) 25 | }) 26 | 27 | test('Raw.lock() throws if arguments invalid', t => { 28 | t.throws(() => Raw.lock(1000, {})) 29 | t.throws(() => Raw.lock(1000, { script: 'Not a script' })) 30 | }) 31 | 32 | test('Raw.unlock() unlocks UTXO with a privkey', t => { 33 | const cast = Raw.unlock(utxo, { script: unlockScript }) 34 | const input = cast.toInput() 35 | const script = cast.toScript() 36 | 37 | t.is(input.txid, utxo.txid) 38 | t.is(input.vout, utxo.vout) 39 | t.deepEqual(input.script, script) 40 | t.regex(script.toASM(), /^[0-9a-f]{142} [0-9a-f]{66}$/) 41 | }) 42 | 43 | test('Raw.unlock() throws if arguments invalid', t => { 44 | t.throws(() => Raw.unlock(utxo, {})) 45 | t.throws(() => Raw.unlock(utxo, { script: 'Not a script' })) 46 | }) 47 | -------------------------------------------------------------------------------- /test/classes/cast.test.js: -------------------------------------------------------------------------------- 1 | import test from 'ava' 2 | import nimble from '@runonbitcoin/nimble' 3 | import { Cast, isCast, toUTXO } from '../../src/index.js' 4 | 5 | const { Transaction, Script } = nimble.classes 6 | const { opcodes } = nimble.constants 7 | const { sha256, sha256d } = nimble.functions 8 | 9 | class HashPuzzle extends Cast { 10 | lockingScript({ secret }) { 11 | this.script 12 | .push(opcodes.OP_SHA256) 13 | .push(sha256d(secret)) 14 | .push(opcodes.OP_EQUAL) 15 | } 16 | 17 | unlockingScript({ secret }) { 18 | this.script.push(sha256(secret)) 19 | } 20 | } 21 | 22 | const secret = 'test secret' 23 | 24 | test('Cast#lock() creates a locking Cast', t => { 25 | const cast = HashPuzzle.lock(1000, { secret }) 26 | t.true(isCast(cast)) 27 | t.is(cast.mode, 'lock') 28 | t.is(cast.satoshis, 1000) 29 | }) 30 | 31 | test('Cast#unlock() creates an unlocking Cast', t => { 32 | const utxo = toUTXO({ 33 | txid: '0000000000000000000000000000000000000000000000000000000000000000', 34 | vout: 0 35 | }) 36 | const cast = HashPuzzle.unlock(utxo, { secret }) 37 | t.true(isCast(cast)) 38 | t.is(cast.mode, 'unlock') 39 | t.is(cast.utxo.txid, '0000000000000000000000000000000000000000000000000000000000000000') 40 | t.is(cast.utxo.vout, 0) 41 | }) 42 | 43 | test('Cast#simulate() returns vm with valid params', t => { 44 | const vm = HashPuzzle.simulate({ secret }, { secret }) 45 | t.true(vm.success) 46 | t.is(vm.error, null) 47 | t.true(Array.isArray(vm.chunks)) 48 | t.true(Array.isArray(vm.stack)) 49 | t.true(Array.isArray(vm.stackTrace)) 50 | }) 51 | 52 | test('Cast#simulate() returns vm with unvalid params', t => { 53 | const vm = HashPuzzle.simulate({ secret }, { secret: 'incorrect' }) 54 | t.false(vm.success) 55 | t.true(vm.error instanceof Error) 56 | }) 57 | 58 | test('Cast#toScript() returns the script', t => { 59 | const cast = HashPuzzle.lock(1000, { secret }) 60 | const script = cast.toScript() 61 | t.true(script instanceof Script) 62 | t.is(script.chunks.length, 3) 63 | }) 64 | 65 | test('Cast#toOutput() returns the Output', t => { 66 | const cast = HashPuzzle.lock(1000, { secret }) 67 | const output = cast.toOutput() 68 | t.true(output instanceof Transaction.Output) 69 | t.is(output.satoshis, 1000) 70 | }) 71 | 72 | test('Cast#toOutput() throws if unlocking cast', t => { 73 | const utxo = toUTXO({ 74 | txid: '0000000000000000000000000000000000000000000000000000000000000000', 75 | vout: 0 76 | }) 77 | const cast = HashPuzzle.unlock(utxo, { secret }) 78 | t.throws(_ => cast.toOutput()) 79 | }) 80 | 81 | test('Cast#toInput() returns the Output', t => { 82 | const utxo = toUTXO({ 83 | txid: '0000000000000000000000000000000000000000000000000000000000000000', 84 | vout: 0 85 | }) 86 | const cast = HashPuzzle.unlock(utxo, { secret }) 87 | const input = cast.toInput() 88 | t.true(input instanceof Transaction.Input) 89 | t.is(input.txid, utxo.txid) 90 | t.is(input.vout, utxo.vout) 91 | }) 92 | 93 | test('Cast#toInput() throws if locking cast', t => { 94 | const cast = HashPuzzle.lock(1000, { secret }) 95 | t.throws(_ => cast.toInput()) 96 | }) 97 | -------------------------------------------------------------------------------- /test/classes/forge.test.js: -------------------------------------------------------------------------------- 1 | import test from 'ava' 2 | import fs from 'fs' 3 | import nimble from '@runonbitcoin/nimble' 4 | import { Forge, casts, createForge, toUTXO, forgeTx } from '../../src/index.js' 5 | 6 | const path = new URL('../vectors/bip69.json', import.meta.url).pathname 7 | const bip69 = JSON.parse(fs.readFileSync(path)) 8 | 9 | const { P2PK, P2PKH, OpReturn, Raw } = casts 10 | const privkey = nimble.PrivateKey.fromRandom() 11 | const pubkey = privkey.toPublicKey() 12 | const address = pubkey.toAddress() 13 | const utxo = { 14 | txid: '0000000000000000000000000000000000000000000000000000000000000000', 15 | vout: 0 16 | } 17 | 18 | test('forge.inputSum gets sum of all inputs', t => { 19 | const forge = new Forge() 20 | t.is(forge.inputSum, 0) 21 | 22 | forge.addInput(P2PK.unlock(toUTXO({ satoshis: 150 }), { privkey })) 23 | forge.addInput(P2PK.unlock(toUTXO({ satoshis: 9500 }), { privkey })) 24 | t.is(forge.inputSum, 9650) 25 | }) 26 | 27 | test('forge.outputSum gets sum of all outputs', t => { 28 | const forge = new Forge() 29 | t.is(forge.outputSum, 0) 30 | 31 | forge.addOutput(P2PK.lock(150, { pubkey })) 32 | forge.addOutput(P2PK.lock(7735, { pubkey })) 33 | t.is(forge.outputSum, 7885) 34 | }) 35 | 36 | test('forge.addInput() adds input casts to forge', t => { 37 | const forge = new Forge() 38 | t.is(forge.inputs.length, 0) 39 | 40 | forge.addInput(P2PK.unlock(toUTXO(), { privkey })) 41 | forge.addInput(P2PK.unlock(toUTXO(), { privkey })) 42 | t.is(forge.inputs.length, 2) 43 | }) 44 | 45 | test('forge.addInput() accepts array of casts', t => { 46 | const forge = new Forge() 47 | t.is(forge.inputs.length, 0) 48 | 49 | forge.addInput([ 50 | P2PK.unlock(toUTXO(), { privkey }), 51 | P2PK.unlock(toUTXO(), { privkey }) 52 | ]) 53 | t.is(forge.inputs.length, 2) 54 | }) 55 | 56 | test('forge.addInput() throws if invalid value given', t => { 57 | const forge = new Forge() 58 | 59 | t.throws(() => forge.addInput('not a cast')) 60 | t.throws(() => forge.addInput(P2PK.lock(150, { pubkey }))) 61 | }) 62 | 63 | test('forge.addOutput() adds output casts to forge', t => { 64 | const forge = new Forge() 65 | t.is(forge.outputs.length, 0) 66 | 67 | forge.addOutput(P2PK.lock(150, { pubkey })) 68 | forge.addOutput(P2PK.lock(150, { pubkey })) 69 | t.is(forge.outputs.length, 2) 70 | }) 71 | 72 | test('forge.addOutput() accepts array of casts', t => { 73 | const forge = new Forge() 74 | t.is(forge.outputs.length, 0) 75 | 76 | forge.addOutput([ 77 | P2PK.lock(150, { pubkey }), 78 | P2PK.lock(150, { pubkey }) 79 | ]) 80 | t.is(forge.outputs.length, 2) 81 | }) 82 | 83 | test('forge.addOutput() throws if invalid value given', t => { 84 | const forge = new Forge() 85 | 86 | t.throws(() => forge.addOutput('not a cast')) 87 | t.throws(() => forge.addOutput(P2PK.unlock(toUTXO(), { privkey }))) 88 | }) 89 | 90 | test('forge.changeTo() sets changeScript to any given class and params', t => { 91 | const forge = new Forge() 92 | forge.changeTo(P2PK, { pubkey }) 93 | 94 | t.true(forge.changeScript instanceof nimble.Script) 95 | t.is(forge.changeScript.length, 35) 96 | }) 97 | 98 | test('forge.changeTo() accepts P2PKH params as sensible default', t => { 99 | const forge = new Forge() 100 | forge.changeTo({ address }) 101 | t.true(forge.changeScript instanceof nimble.Script) 102 | t.is(forge.changeScript.length, 25) 103 | }) 104 | 105 | test('forge.changeTo() accepts Raw params as sensible default', t => { 106 | const forge = new Forge() 107 | forge.changeTo({ script: 'OP_TRUE' }) 108 | t.true(forge.changeScript instanceof nimble.Script) 109 | t.is(forge.changeScript.length, 1) 110 | }) 111 | 112 | test('forge.changeTo() throws error if invalid cast params given', t => { 113 | const forge = new Forge() 114 | t.throws(_ => forge.changeTo({ foo: 'bar' })) 115 | }) 116 | 117 | function assertBetween(val, min, max) { 118 | if (val < min || val > max) { 119 | throw new Error(`value not within range: ${val} != ${min} < ${max}`) 120 | } 121 | } 122 | 123 | test('forge.calcRequiredFee() calculates the required fee for the tx', t => { 124 | const forge = new Forge({ 125 | inputs: [ 126 | P2PK.unlock(toUTXO({...utxo, satoshis: 10000, script: '01'}), { privkey }) 127 | ], 128 | outputs: [ 129 | P2PK.lock(5000, { pubkey }), 130 | P2PK.lock(1000, { pubkey }) 131 | ] 132 | }) 133 | 134 | const bytes = forge.toTx().toBuffer().length 135 | t.is(forge.calcRequiredFee(), Math.ceil(bytes / 20)) 136 | assertBetween(forge.calcRequiredFee(1000), bytes-1, bytes+1) 137 | t.pass() 138 | }) 139 | 140 | test('forge.calcRequiredFee() calculates data scripts separately', t => { 141 | const forge = new Forge({ 142 | outputs: [ 143 | OpReturn.lock(0, { data: new Uint8Array(1000) }) 144 | ] 145 | }) 146 | 147 | const bytes = forge.toTx().toBuffer().length 148 | t.is(forge.calcRequiredFee(1000), bytes) 149 | t.is(forge.calcRequiredFee({ standard: 1000, data: 100}), bytes-914) 150 | t.pass() 151 | }) 152 | 153 | test('forge.sort() sorts inputs as per bip69', t => { 154 | for (let v of bip69.inputs) { 155 | const inputs = v.inputs.map(i => { 156 | const utxo = toUTXO({ txid: i.txId, vout: i.vout }) 157 | return P2PK.unlock(utxo, { privkey }) 158 | }) 159 | 160 | const forge = new Forge({ inputs }).sort() 161 | const indexes = forge.inputs.map(i => inputs.indexOf(i)) 162 | t.deepEqual(indexes, v.expected, v.description) 163 | } 164 | }) 165 | 166 | test('forge.sort() sorts outputs as per bip69', t => { 167 | for (let v of bip69.outputs) { 168 | const outputs = v.outputs.map(o => { 169 | return Raw.lock(o.value, { script: o.script }) 170 | }) 171 | 172 | const forge = new Forge({ outputs }).sort() 173 | const indexes = forge.outputs.map(i => outputs.indexOf(i)) 174 | t.deepEqual(indexes, v.expected, v.description) 175 | } 176 | }) 177 | 178 | test('forge.toTx() returns signed tx', t => { 179 | const forge = new Forge({ 180 | inputs: [ 181 | P2PK.unlock(toUTXO({...utxo, satoshis: 10000, script: '01'}), { privkey }) 182 | ], 183 | outputs: [ 184 | P2PK.lock(5000, { pubkey }), 185 | P2PK.lock(1000, { pubkey }) 186 | ] 187 | }) 188 | 189 | const tx = forge.toTx() 190 | t.true(tx instanceof nimble.Transaction) 191 | t.is(tx.outputs.length, 2) 192 | t.is(tx.fee, 4000) 193 | }) 194 | 195 | test('forge.toTx() returns signed tx with change', t => { 196 | const forge = new Forge({ 197 | inputs: [ 198 | P2PK.unlock(toUTXO({...utxo, satoshis: 10000, script: '01'}), { privkey }) 199 | ], 200 | outputs: [ 201 | P2PK.lock(5000, { pubkey }), 202 | P2PK.lock(1000, { pubkey }) 203 | ], 204 | change: { address } 205 | }) 206 | 207 | const tx = forge.toTx() 208 | t.is(tx.outputs.length, 3) 209 | t.is(tx.outputs[2].satoshis, 3987) 210 | t.is(tx.fee, 13) 211 | }) 212 | 213 | test('createForge() sets given inputs and outputs', t => { 214 | const forge = createForge({ 215 | inputs: [ 216 | P2PKH.unlock(toUTXO(), { privkey }), 217 | P2PK.unlock(toUTXO(), { privkey }) 218 | ], 219 | outputs: [ 220 | P2PKH.lock(10000, { address }), 221 | P2PK.lock(10000, { pubkey }), 222 | OpReturn.lock(0, { data: 'hello world' }) 223 | ] 224 | }) 225 | 226 | t.is(forge.inputs.length, 2) 227 | t.is(forge.outputs.length, 3) 228 | t.is(forge.outputSum, 20000) 229 | }) 230 | 231 | test('createForge() sets given change param tuple', t => { 232 | const forge = createForge({ 233 | change: [P2PK, { pubkey }] 234 | }) 235 | 236 | t.true(forge.changeScript instanceof nimble.Script) 237 | t.is(forge.changeScript.length, 35) 238 | }) 239 | 240 | test('createForge() sets given change param as P2PKH script', t => { 241 | const forge = createForge({ 242 | change: { address } 243 | }) 244 | 245 | t.true(forge.changeScript instanceof nimble.Script) 246 | t.is(forge.changeScript.length, 25) 247 | }) 248 | 249 | test('createForge() sets given change param as Raw script', t => { 250 | const forge = createForge({ 251 | change: { script: 'OP_TRUE' } 252 | }) 253 | 254 | t.true(forge.changeScript instanceof nimble.Script) 255 | t.is(forge.changeScript.length, 1) 256 | }) 257 | 258 | test('createForge() sets given locktime', t => { 259 | const forge = createForge({ 260 | locktime: 999999 261 | }) 262 | 263 | t.is(forge.locktime, 999999) 264 | }) 265 | 266 | test('createForge() sets given options', t => { 267 | const forge = createForge({ 268 | options: { sort: true, foo: 'bar' } 269 | }) 270 | 271 | t.is(forge.options.sort, true) 272 | t.is(forge.options.foo, 'bar') 273 | t.deepEqual(forge.options.rates, { standard: 50, data: 50 }) 274 | }) 275 | 276 | test('forgeTx() returns a built transaction', t => { 277 | const txid = '0000000000000000000000000000000000000000000000000000000000000000' 278 | const tx = forgeTx({ 279 | inputs: [ 280 | P2PKH.unlock(toUTXO({ txid, vout: 0, satoshis: 10000, script: '006a' }), { privkey }), 281 | P2PK.unlock(toUTXO({ txid, vout: 0, satoshis: 10000, script: '006a' }), { privkey }) 282 | ], 283 | outputs: [ 284 | P2PKH.lock(10000, { address }), 285 | P2PK.lock(10000, { pubkey }), 286 | OpReturn.lock(0, { data: 'hello world' }) 287 | ] 288 | }) 289 | 290 | t.is(tx.inputs.length, 2) 291 | t.is(tx.outputs.length, 3) 292 | }) 293 | 294 | test('forgeTx() sets change on the tx', t => { 295 | const txid = '0000000000000000000000000000000000000000000000000000000000000000' 296 | const tx = forgeTx({ 297 | inputs: [ 298 | P2PKH.unlock(toUTXO({ txid, vout: 0, satoshis: 10000, script: '006a' }), { privkey }), 299 | ], 300 | outputs: [ 301 | P2PKH.lock(1000, { address }), 302 | ], 303 | change: { address } 304 | }) 305 | 306 | t.is(tx.inputs.length, 1) 307 | t.is(tx.outputs.length, 2) 308 | }) 309 | 310 | test('forgeTx() sets locktime on the tx', t => { 311 | const tx = forgeTx({ 312 | locktime: 999999 313 | }) 314 | 315 | t.is(tx.locktime, 999999) 316 | }) 317 | 318 | test('forgeTx() uses given rates', t => { 319 | const txid = '0000000000000000000000000000000000000000000000000000000000000000' 320 | const tx = forgeTx({ 321 | inputs: [ 322 | P2PKH.unlock(toUTXO({ txid, vout: 0, satoshis: 10000, script: '006a' }), { privkey }), 323 | ], 324 | outputs: [ 325 | OpReturn.lock(0, { data: new Array(1000).fill(0) }), 326 | ], 327 | change: { address }, 328 | options: { rates: 1000 } 329 | }) 330 | 331 | t.true(tx.fee > 1200) 332 | }) 333 | -------------------------------------------------------------------------------- /test/classes/shared.test.js: -------------------------------------------------------------------------------- 1 | import test from 'ava' 2 | import { getOpt } from '../../src/classes/shared.js' 3 | 4 | test('getOpt() gets the first found value from the list of keys', t => { 5 | const params = { foo: 1, bar: 0 } 6 | t.is(getOpt(params, ['foo', 'bar']), 1) 7 | t.is(getOpt(params, ['barx', 'bar', 'foo']), 0) 8 | }) 9 | 10 | test('getOpt() returns undefined if no value found from keys', t => { 11 | const params = { foo: 1, bar: 0 } 12 | t.is(getOpt(params, ['foo1', 'foo2', 'foo3']), undefined) 13 | }) 14 | 15 | test('getOpt() returns default if no value found from keys', t => { 16 | const params = { foo: 1, bar: 0 } 17 | t.is(getOpt(params, ['foo1', 'foo2', 'foo3'], 999), 999) 18 | }) 19 | -------------------------------------------------------------------------------- /test/classes/tape.test.js: -------------------------------------------------------------------------------- 1 | import test from 'ava' 2 | import nimble from '@runonbitcoin/nimble' 3 | import { Tape, toScript } from '../../src/index.js' 4 | 5 | const { opcodes } = nimble.constants 6 | 7 | test('Tape.push() pushes an opcode onto the tape', t => { 8 | const tape = new Tape() 9 | tape.push(opcodes.OP_RETURN) 10 | t.deepEqual(tape.cells, [{ opcode: 106 }]) 11 | }) 12 | 13 | test('Tape.push() pushes a buffer onto the tape', t => { 14 | const tape = new Tape() 15 | tape.push(Array.from(new Uint8Array([1,2,3,4]))) 16 | t.deepEqual(tape.cells, [{ buf: [1,2,3,4] }]) 17 | }) 18 | 19 | test('Tape.push() pushes a string onto the tape', t => { 20 | const tape = new Tape() 21 | tape.push('hello') 22 | t.true(tape.cells[0].buf instanceof Uint8Array) 23 | }) 24 | 25 | test('Tape.push() pushes an array of elements onto the tape', t => { 26 | const tape = new Tape() 27 | tape.push([opcodes.OP_RETURN, 'hello', new Uint8Array([1,2,3,4])]) 28 | t.is(tape.cells.length, 3) 29 | }) 30 | 31 | test('Tape.push() throws if an invalid integer', t => { 32 | const tape = new Tape() 33 | t.throws(_ => tape.push(999)) 34 | }) 35 | 36 | test('Tape.push() throws if an unknown type', t => { 37 | const tape = new Tape() 38 | t.throws(_ => tape.push({ foo: 'bar' })) 39 | }) 40 | 41 | 42 | test('Tape.apply() applies the macro to the tape', t => { 43 | function macro(code) { 44 | this.script.push(code) 45 | } 46 | const tape = new Tape() 47 | tape.apply(macro, [opcodes.OP_RETURN]) 48 | 49 | t.deepEqual(tape.cells, [{ opcode: 106 }]) 50 | }) 51 | 52 | test('Tape.apply() throws helpful error if arguments not an array', t => { 53 | function macro(code) { 54 | this.script.push(code) 55 | } 56 | const tape = new Tape() 57 | t.throws(_ => tape.apply(macro, opcodes.OP_RETURN), { message: /^invalid args/ }) 58 | }) 59 | 60 | test('Tape.each() iterates over the elements', t => { 61 | const tape = new Tape() 62 | tape.each([1,2,3], (el, i) => tape.push([el, i])) 63 | t.is(tape.cells.length, 3) 64 | t.deepEqual(tape.cells[0], { buf: [1, 0] }) 65 | }) 66 | 67 | test('Tape.repeats() iterates the specified number of times', t => { 68 | const tape = new Tape() 69 | tape.repeat(3, (i) => tape.push([i])) 70 | t.is(tape.cells.length, 3) 71 | t.deepEqual(tape.cells[0], { buf: [0] }) 72 | }) 73 | 74 | test('toScript() converts the tape to a script', t => { 75 | const tape = new Tape() 76 | tape.push(opcodes.OP_RETURN).push('hello') 77 | const script = toScript(tape) 78 | 79 | t.true(script instanceof nimble.Script) 80 | t.is(script.toHex(), '6a0568656c6c6f') 81 | }) 82 | 83 | test('toScript() throws if the argument is not a tape', t => { 84 | t.throws(_ => toScript('hello'), { message: /^invalid argument/ }) 85 | }) -------------------------------------------------------------------------------- /test/classes/utxo.test.js: -------------------------------------------------------------------------------- 1 | import test from 'ava' 2 | import nimble from '@runonbitcoin/nimble' 3 | import { toUTXO, getUTXO, forgeTx, casts} from '../../src/index.js' 4 | 5 | const { Transaction, Script } = nimble.classes 6 | 7 | test('toUTXO() accepts Mattercloud api params', t => { 8 | const params = { 9 | "address": "12XXBHkRNrBEb7GCvAP4G8oUs5SoDREkVX", 10 | "txid": "5e3014372338f079f005eedc85359e4d96b8440e7dbeb8c35c4182e0c19a1a12", 11 | "vout": 0, 12 | "amount": 0.00015399, 13 | "satoshis": 15399, 14 | "value": 15399, 15 | "height": 576168, 16 | "confirmations": 34993, 17 | "scriptPubKey": "76a91410bdcba3041b5e5517a58f2e405293c14a7c70c188ac", 18 | "script": "76a91410bdcba3041b5e5517a58f2e405293c14a7c70c188ac", 19 | "outputIndex": 0 20 | } 21 | 22 | const utxo = toUTXO(params) 23 | t.is(utxo.txid, params.txid) 24 | t.is(utxo.vout, params.vout) 25 | t.is(utxo.satoshis, params.satoshis) 26 | t.true(utxo.script instanceof Script) 27 | t.true(utxo.output instanceof Transaction.Output) 28 | }) 29 | 30 | test('toUTXO() accepts WOC api params', t => { 31 | const params = { 32 | "height": 578325, 33 | "tx_pos": 0, 34 | "tx_hash": "62824e3af3d01113e9bce8b73576b833990d231357bd718385958c21d50bbddd", 35 | "value": 1250020815 36 | } 37 | 38 | const utxo = toUTXO(params) 39 | t.is(utxo.txid, params.tx_hash) 40 | t.is(utxo.vout, params.tx_pos) 41 | t.is(utxo.satoshis, params.value) 42 | }) 43 | 44 | test('getUTXO() gets UTXO from the given tx and vout', t => { 45 | const tx = forgeTx({ 46 | outputs: [casts.Raw.lock(1000, { script: new Script() })] 47 | }) 48 | 49 | const utxo = getUTXO(tx, 0) 50 | t.is(utxo.txid, tx.hash) 51 | t.is(utxo.vout, 0) 52 | t.is(utxo.satoshis, 1000) 53 | t.true(utxo.script instanceof Script) 54 | t.true(utxo.output instanceof Transaction.Output) 55 | }) 56 | -------------------------------------------------------------------------------- /test/extra/r-puzzle.test.js: -------------------------------------------------------------------------------- 1 | import test from 'ava' 2 | import { generateK, calculateR } from '../../src/extra/r-puzzle.js' 3 | 4 | test('generateK() returns random K value', t => { 5 | const k = generateK() 6 | t.true(k instanceof Uint8Array) 7 | t.is(k.length, 32) 8 | }) 9 | 10 | test('calculateR() returns R from K', t => { 11 | const k = generateK() 12 | const r = calculateR(k) 13 | t.true(r instanceof Uint8Array) 14 | t.true([32,33].includes(r.length)) 15 | }) 16 | -------------------------------------------------------------------------------- /test/helpers/asm.test.js: -------------------------------------------------------------------------------- 1 | import test from 'ava' 2 | import { asm } from '../../src/helpers/index.js' 3 | 4 | const str = 'OP_DUP OP_HASH160 5ae866af9de106847de6111e5f1faa168b2be689 OP_EQUALVERIFY OP_CHECKSIG' 5 | 6 | test('asm() converts asm string to script chunks', t => { 7 | const chunks = asm(str) 8 | t.is(chunks.length, 5) 9 | t.is(chunks[0].opcode, 118) 10 | t.is(chunks[1].opcode, 169) 11 | t.is(chunks[3].opcode, 136) 12 | t.is(chunks[4].opcode, 172) 13 | }) 14 | 15 | test('asm() throws with invalid asm string', t => { 16 | t.throws(_ => asm('OP_RETURN xyz OP_0')) 17 | }) 18 | -------------------------------------------------------------------------------- /test/helpers/num.test.js: -------------------------------------------------------------------------------- 1 | import test from 'ava' 2 | import { num } from '../../src/helpers/index.js' 3 | 4 | test('num() returns integer for small ints', t => { 5 | t.is(num(0), 0) 6 | t.is(num(1), 81) 7 | t.is(num(10), 90) 8 | t.is(num(16), 96) 9 | }) 10 | 11 | test('num() returns byte vector for larger ints ints', t => { 12 | t.deepEqual(num(17), [17]) 13 | t.deepEqual(num(3200), [128, 12]) 14 | }) 15 | -------------------------------------------------------------------------------- /test/macros/binary.test.js: -------------------------------------------------------------------------------- 1 | import test from 'ava' 2 | import nimble from '@runonbitcoin/nimble' 3 | import { Tape, toScript } from '../../src/classes/tape.js' 4 | import { decodeUint, reverse, slice, trim } from '../../src/macros/index.js' 5 | 6 | const { evalScript, generateRandomData } = nimble.functions 7 | 8 | test('decodeUint() casts the top stack element to a script number', t => { 9 | function uintBuf(len, cb) { 10 | const buf = new ArrayBuffer(len) 11 | const view = new DataView(buf) 12 | cb(view) 13 | return new Uint8Array(view.buffer) 14 | } 15 | 16 | const bufs = [ 17 | uintBuf(1, v => v.setUint8(0, 24, true)), 18 | uintBuf(4, v => v.setUint32(0, 24, true)), 19 | uintBuf(4, v => v.setUint32(0, 4000000000, true)), 20 | ] 21 | 22 | const b = new Tape() 23 | b.each(bufs, buf => { 24 | b.push(buf).apply(decodeUint) 25 | }) 26 | 27 | const script = toScript(b) 28 | const { stack } = evalScript([], script) 29 | 30 | const expected = [[24], [24], [0, 40, 107, 238, 0]] 31 | t.deepEqual(stack, expected) 32 | }) 33 | 34 | test('reverse() reverses the data on top of the stack', t => { 35 | const bufs = [ 36 | [1,2,3,4,5], 37 | [1,2,3,4,5,6,7,8], 38 | [1,2,3,4,5,6,7,8,9,10,11,12], 39 | [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16], 40 | [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25], 41 | [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32], 42 | [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34], 43 | Array.from(generateRandomData(560)) 44 | ] 45 | t.plan(bufs.length) 46 | 47 | for (let buf of bufs) { 48 | const b = new Tape() 49 | b.push(buf) 50 | b.apply(reverse, [buf.length]) 51 | 52 | const script = toScript(b) 53 | const { stack } = evalScript([], script) 54 | 55 | t.deepEqual(stack[0], buf.reverse()) 56 | } 57 | }) 58 | 59 | test('slice() slices bytes from the top of the stack', t => { 60 | t.plan(5) 61 | 62 | // When start has positive index 63 | for (let args of [[0, 2], [4, 4], [13, 2]]) { 64 | const buf = new Uint8Array([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]) 65 | const b = new Tape() 66 | b.push(buf) 67 | b.apply(slice, args) 68 | 69 | const script = toScript(b) 70 | const { stack } = evalScript([], script) 71 | 72 | const expected = buf.slice(args[0], args[0] + args[1]) 73 | t.deepEqual(stack[0], expected) 74 | } 75 | 76 | // When start has negative index 77 | for (let args of [[-4, 4], [-13, 2]]) { 78 | const buf = new Uint8Array([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]) 79 | const b = new Tape() 80 | b.push(buf) 81 | b.apply(slice, args) 82 | 83 | const script = toScript(b) 84 | const { stack } = evalScript([], script) 85 | 86 | const expected = buf.slice(buf.length + args[0], buf.length + args[0] + args[1]) 87 | t.deepEqual(stack[0], expected) 88 | } 89 | }) 90 | 91 | test('trim() trims leading bytes from the top of the stack', t => { 92 | t.plan(4) 93 | 94 | for (let arg of [2, 4, 8, 13]) { 95 | const buf = new Uint8Array([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]) 96 | const b = new Tape() 97 | b.push(buf) 98 | b.apply(trim, [arg]) 99 | 100 | const script = toScript(b) 101 | const { stack } = evalScript([], script) 102 | 103 | const expected = buf.slice(arg) 104 | t.deepEqual(stack[0], expected) 105 | } 106 | }) 107 | 108 | test('trim() trims trailing bytes from the top of the stack', t => { 109 | t.plan(4) 110 | 111 | for (let arg of [-2, -4, -8, -13]) { 112 | const buf = new Uint8Array([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]) 113 | const b = new Tape() 114 | b.push(buf) 115 | b.apply(trim, [arg]) 116 | 117 | const script = toScript(b) 118 | const { stack } = evalScript([], script) 119 | 120 | const expected = buf.slice(0, buf.length+arg) 121 | t.deepEqual(stack[0], expected) 122 | } 123 | }) -------------------------------------------------------------------------------- /test/macros/push-tx.test.js: -------------------------------------------------------------------------------- 1 | import test from 'ava' 2 | import nimble from '@runonbitcoin/nimble' 3 | import { casts, forgeTx, Cast, getUTXO } from '../../src/index.js' 4 | import { Tape, toScript } from '../../src/classes/tape.js' 5 | import { num } from '../../src/helpers/index.js' 6 | import { 7 | getVersion, 8 | getPrevoutsHash, 9 | getSequenceHash, 10 | getOutpoint, 11 | getScript, 12 | getSatoshis, 13 | getSequence, 14 | getOutputsHash, 15 | getLocktime, 16 | getSighashType, 17 | pushTx, 18 | checkTx, 19 | checkTxVerify, 20 | checkTxOpt, 21 | } from '../../src/macros/index.js' 22 | 23 | const { Script } = nimble.classes 24 | const { decodeHex, evalScript, preimage } = nimble.functions 25 | const { OP_DROP } = nimble.constants.opcodes 26 | 27 | const prevTx = forgeTx({ 28 | outputs: [ 29 | casts.P2PKH.lock(50000, { address: '15KgnG69mTbtkx73vNDNUdrWuDhnmfCxsf' }) 30 | ] 31 | }) 32 | 33 | const utxo = getUTXO(prevTx, 0) 34 | 35 | const testTx = forgeTx({ 36 | inputs: [ 37 | casts.Raw.unlock(utxo, { script: new Script() }) 38 | ] 39 | }) 40 | 41 | const prevOut = prevTx.outputs[0] 42 | const preimg = preimage(testTx, 0, prevOut.script, prevOut.satoshis, 0x41) 43 | 44 | 45 | test('getVersion() puts tx version on top of stack', t => { 46 | const b = new Tape() 47 | b.push(preimg).apply(getVersion) 48 | const script = toScript(b) 49 | const { stack } = evalScript([], script) 50 | 51 | t.deepEqual(stack[stack.length-1], [1]) 52 | }) 53 | 54 | test('getPrevoutsHash() puts prev outpoints hash on top of stack', t => { 55 | const b = new Tape() 56 | b.push(preimg).apply(getPrevoutsHash) 57 | const script = toScript(b) 58 | const { stack } = evalScript([], script) 59 | 60 | const expected = Array.from(testTx._hashPrevouts) 61 | t.deepEqual(stack[stack.length-1], expected) 62 | }) 63 | 64 | test('getSequenceHash() puts txin sequence hash on top of stack', t => { 65 | const b = new Tape() 66 | b.push(preimg).apply(getSequenceHash) 67 | const script = toScript(b) 68 | const { stack } = evalScript([], script) 69 | 70 | const expected = Array.from(testTx._hashSequence) 71 | t.deepEqual(stack[stack.length-1], expected) 72 | }) 73 | 74 | test('getOutpoint() puts txin outpoint on top of stack', t => { 75 | const b = new Tape() 76 | b.push(preimg).apply(getOutpoint) 77 | const script = toScript(b) 78 | const { stack } = evalScript([], script) 79 | 80 | const expected = Array.from(decodeHex(prevTx.hash).reverse()).concat([0,0,0,0]) 81 | t.deepEqual(stack[stack.length-1], expected) 82 | }) 83 | 84 | test('getScript() puts lock script on top of stack', t => { 85 | const b = new Tape() 86 | b.push(preimg).apply(getScript) 87 | const script = toScript(b) 88 | const { stack } = evalScript([], script) 89 | 90 | t.deepEqual(stack[stack.length-1], Array.from(prevOut.script.buffer)) 91 | }) 92 | 93 | test('getSatoshis() puts lock satoshis on top of stack', t => { 94 | const b = new Tape() 95 | b.push(preimg).apply(getSatoshis) 96 | const script = toScript(b) 97 | const { stack } = evalScript([], script) 98 | 99 | t.deepEqual(stack[stack.length-1], num(50000)) 100 | }) 101 | 102 | test('getSequence() puts txin sequence on top of stack', t => { 103 | const b = new Tape() 104 | b.push(preimg).apply(getSequence) 105 | const script = toScript(b) 106 | const { stack } = evalScript([], script) 107 | 108 | t.deepEqual(stack[stack.length-1], num(0xFFFFFFFF)) 109 | }) 110 | 111 | test('getOutputsHash() puts tx outputs hash on top of stack', t => { 112 | const b = new Tape() 113 | b.push(preimg).apply(getOutputsHash) 114 | const script = toScript(b) 115 | const { stack } = evalScript([], script) 116 | 117 | const expected = Array.from(testTx._hashOutputsAll) 118 | t.deepEqual(stack[stack.length-1], expected) 119 | }) 120 | 121 | test('getLocktime() puts tx locktime on top of stack', t => { 122 | const b = new Tape() 123 | b.push(preimg).apply(getLocktime).push([1]) 124 | const script = toScript(b) 125 | const { stack } = evalScript([], script) 126 | 127 | t.deepEqual(stack[stack.length-2], []) 128 | }) 129 | 130 | test('getSighashType() puts tx sighash type on top of stack', t => { 131 | const b = new Tape() 132 | b.push(preimg).apply(getSighashType) 133 | const script = toScript(b) 134 | const { stack } = evalScript([], script) 135 | 136 | t.deepEqual(stack[stack.length-1], [0x41]) 137 | }) 138 | 139 | test('pushTx() pushes the tx preimage onto the stack', t => { 140 | const b = new Tape() 141 | b.cast.ctx = { tx: testTx, vin: 0 } 142 | b.cast.utxo = utxo 143 | b.apply(pushTx) 144 | const script = toScript(b) 145 | const { stack } = evalScript(script, []) 146 | 147 | t.deepEqual(stack[0], preimg) 148 | }) 149 | 150 | test('pushTx() pushes zero bytes placeholder onto the stack without context', t => { 151 | const b = new Tape() 152 | b.apply(pushTx).push([1]) 153 | const script = toScript(b) 154 | const { stack } = evalScript(script, []) 155 | 156 | t.deepEqual(stack[0], new Uint8Array(181).fill(0)) 157 | }) 158 | 159 | class TestCast extends Cast { 160 | lockingScript({ extraBytes, optimized, verify }) { 161 | if (extraBytes) { 162 | this.script.push([0]).push(OP_DROP) 163 | } 164 | if (optimized) { 165 | this.script.apply(checkTxOpt) 166 | } else if (verify) { 167 | this.script.apply(checkTxVerify).push([1,2,3]) 168 | } else { 169 | this.script.apply(checkTx) 170 | } 171 | } 172 | 173 | unlockingScript() { 174 | this.script.apply(pushTx) 175 | } 176 | } 177 | 178 | test('TestCast simulates full check tx', t => { 179 | const vm = TestCast.simulate() 180 | t.true(vm.success) 181 | }) 182 | 183 | test('TestCast simulates full check tx verify', t => { 184 | const vm = TestCast.simulate({ verify: true }) 185 | t.true(vm.success) 186 | }) 187 | 188 | test('TestCast simulates optimal check tx verify', t => { 189 | const vm = TestCast.simulate({ optimized: true }) 190 | t.true(vm.success) 191 | }) 192 | 193 | test('TestCast optimal check has 50% chance of not working', t => { 194 | const vm = TestCast.simulate({ optimized: true, extraBytes: true }) 195 | t.false(vm.success) 196 | t.truthy(vm.error) 197 | }) 198 | -------------------------------------------------------------------------------- /test/macros/varint.test.js: -------------------------------------------------------------------------------- 1 | import test from 'ava' 2 | import nimble from '@runonbitcoin/nimble' 3 | import { Tape, toScript } from '../../src/classes/tape.js' 4 | import { num } from '../../src/helpers/index.js' 5 | import { getVarint, readVarint, trimVarint } from '../../src/macros/index.js' 6 | 7 | const { BufferWriter } = nimble.classes 8 | const { evalScript, writeVarint } = nimble.functions 9 | 10 | test('getVarint() gets the varint from the top stack element and puts on top', t => { 11 | for (let bytes of [32, 320, 320000]) { 12 | const data = new Uint8Array(bytes).fill(1) 13 | const buf = new BufferWriter() 14 | writeVarint(buf, data.length) 15 | buf.write(data) 16 | const b = new Tape() 17 | b.push(buf).apply(getVarint) 18 | 19 | const script = toScript(b) 20 | const { stack } = evalScript([], script) 21 | 22 | const expected = [buf.toBuffer(), num(bytes)] 23 | t.deepEqual(stack, expected) 24 | } 25 | }) 26 | 27 | test('readVarint() reads the varint from the top stack item and puts the encoded data on top', t => { 28 | for (let bytes of [32, 320, 320000]) { 29 | const data = new Uint8Array(bytes).fill(1) 30 | const buf = new BufferWriter() 31 | writeVarint(buf, data.length) 32 | buf.write(data) 33 | buf.write([1,2,3,4]) 34 | const b = new Tape() 35 | b.push(buf).apply(readVarint) 36 | 37 | const script = toScript(b) 38 | const { stack } = evalScript([], script) 39 | 40 | const expected = [new Uint8Array([1, 2, 3, 4]), data] 41 | t.deepEqual(stack, expected) 42 | } 43 | }) 44 | 45 | test('trimVarint() trims the varint from the leading bytes of the top stack item', t => { 46 | for (let bytes of [32, 320, 320000]) { 47 | const data = new Uint8Array(bytes).fill(1) 48 | const buf = new BufferWriter() 49 | writeVarint(buf, data.length) 50 | buf.write(data) 51 | const b = new Tape() 52 | b.push(buf).apply(trimVarint) 53 | 54 | const script = toScript(b) 55 | const { stack } = evalScript([], script) 56 | 57 | const expected = [data] 58 | t.deepEqual(stack, expected) 59 | } 60 | }) 61 | -------------------------------------------------------------------------------- /test/vectors/bip69.json: -------------------------------------------------------------------------------- 1 | { 2 | "inputs": [ 3 | { 4 | "description": "Ordered by txId, descending (reverse-byte-order ascending)", 5 | "inputs": [ 6 | { 7 | "txId": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 8 | "vout": 0 9 | }, 10 | { 11 | "txId": "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc", 12 | "vout": 0 13 | }, 14 | { 15 | "txId": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", 16 | "vout": 0 17 | }, 18 | { 19 | "txId": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbff", 20 | "vout": 0 21 | }, 22 | { 23 | "txId": "ffbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", 24 | "vout": 0 25 | } 26 | ], 27 | "expected": [0, 2, 3, 1, 4] 28 | }, 29 | { 30 | "description": "Ordered by vout, ascending", 31 | "inputs": [ 32 | { 33 | "txId": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 34 | "vout": 1 35 | }, 36 | { 37 | "txId": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 38 | "vout": 2 39 | }, 40 | { 41 | "txId": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 42 | "vout": 0 43 | } 44 | ], 45 | "expected": [2, 0, 1] 46 | }, 47 | { 48 | "description": "Ordered by txId, then vout", 49 | "inputs": [ 50 | { 51 | "txId": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 52 | "vout": 99 53 | }, 54 | { 55 | "txId": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", 56 | "vout": 99 57 | }, 58 | { 59 | "txId": "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc", 60 | "vout": 0 61 | }, 62 | { 63 | "txId": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", 64 | "vout": 0 65 | } 66 | ], 67 | "expected": [0, 3, 1, 2] 68 | }, 69 | { 70 | "description": "BIP69 test vector 1", 71 | "inputs": [ 72 | { "txId": "0e53ec5dfb2cb8a71fec32dc9a634a35b7e24799295ddd5278217822e0b31f57", "vout": 0 }, 73 | { "txId": "26aa6e6d8b9e49bb0630aac301db6757c02e3619feb4ee0eea81eb1672947024", "vout": 1 }, 74 | { "txId": "28e0fdd185542f2c6ea19030b0796051e7772b6026dd5ddccd7a2f93b73e6fc2", "vout": 0 }, 75 | { "txId": "381de9b9ae1a94d9c17f6a08ef9d341a5ce29e2e60c36a52d333ff6203e58d5d", "vout": 1 }, 76 | { "txId": "3b8b2f8efceb60ba78ca8bba206a137f14cb5ea4035e761ee204302d46b98de2", "vout": 0 }, 77 | { "txId": "402b2c02411720bf409eff60d05adad684f135838962823f3614cc657dd7bc0a", "vout": 1 }, 78 | { "txId": "54ffff182965ed0957dba1239c27164ace5a73c9b62a660c74b7b7f15ff61e7a", "vout": 1 }, 79 | { "txId": "643e5f4e66373a57251fb173151e838ccd27d279aca882997e005016bb53d5aa", "vout": 0 }, 80 | { "txId": "6c1d56f31b2de4bfc6aaea28396b333102b1f600da9c6d6149e96ca43f1102b1", "vout": 1 }, 81 | { "txId": "7a1de137cbafb5c70405455c49c5104ca3057a1f1243e6563bb9245c9c88c191", "vout": 0 }, 82 | { "txId": "7d037ceb2ee0dc03e82f17be7935d238b35d1deabf953a892a4507bfbeeb3ba4", "vout": 1 }, 83 | { "txId": "a5e899dddb28776ea9ddac0a502316d53a4a3fca607c72f66c470e0412e34086", "vout": 0 }, 84 | { "txId": "b4112b8f900a7ca0c8b0e7c4dfad35c6be5f6be46b3458974988e1cdb2fa61b8", "vout": 0 }, 85 | { "txId": "bafd65e3c7f3f9fdfdc1ddb026131b278c3be1af90a4a6ffa78c4658f9ec0c85", "vout": 0 }, 86 | { "txId": "de0411a1e97484a2804ff1dbde260ac19de841bebad1880c782941aca883b4e9", "vout": 1 }, 87 | { "txId": "f0a130a84912d03c1d284974f563c5949ac13f8342b8112edff52971599e6a45", "vout": 0 }, 88 | { "txId": "f320832a9d2e2452af63154bc687493484a0e7745ebd3aaf9ca19eb80834ad60", "vout": 0 } 89 | ], 90 | "expected": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16] 91 | }, 92 | { 93 | "description": "BIP69 test vector 2", 94 | "inputs": [ 95 | { "txId": "35288d269cee1941eaebb2ea85e32b42cdb2b04284a56d8b14dcc3f5c65d6055", "vout": 0 }, 96 | { "txId": "35288d269cee1941eaebb2ea85e32b42cdb2b04284a56d8b14dcc3f5c65d6055", "vout": 1 } 97 | ], 98 | "expected": [0, 1] 99 | } 100 | ], 101 | "outputs": [ 102 | { 103 | "description": "Ordered by Amount, ascending", 104 | "outputs": [ 105 | { 106 | "script": "0400000000", 107 | "value": 3000 108 | }, 109 | { 110 | "script": "0400000000", 111 | "value": 2000 112 | }, 113 | { 114 | "script": "0400000000", 115 | "value": 1000 116 | } 117 | ], 118 | "expected": [2, 1, 0] 119 | }, 120 | { 121 | "description": "Ordered by Script, ascending", 122 | "outputs": [ 123 | { 124 | "script": "0400000000", 125 | "value": 1000 126 | }, 127 | { 128 | "script": "0422222222", 129 | "value": 1000 130 | }, 131 | { 132 | "script": "0411111111", 133 | "value": 1000 134 | } 135 | ], 136 | "expected": [0, 2, 1] 137 | }, 138 | { 139 | "description": "Ordered by Amount, then Script", 140 | "outputs": [ 141 | { 142 | "script": "0411111111", 143 | "value": 1000 144 | }, 145 | { 146 | "script": "0411111111", 147 | "value": 2000 148 | }, 149 | { 150 | "script": "0400000000", 151 | "value": 3000 152 | }, 153 | { 154 | "script": "0400000000", 155 | "value": 2000 156 | } 157 | ], 158 | "expected": [0, 3, 1, 2] 159 | }, 160 | { 161 | "description": "BIP69 test vector 1", 162 | "outputs": [ 163 | { 164 | "script": "76a9144a5fba237213a062f6f57978f796390bdcf8d01588ac", 165 | "value": 400057456 166 | }, 167 | { 168 | "script": "76a9145be32612930b8323add2212a4ec03c1562084f8488ac", 169 | "value": 40000000000 170 | } 171 | ], 172 | "expected": [0, 1] 173 | }, 174 | { 175 | "description": "BIP69 test vector 2", 176 | "outputs": [ 177 | { 178 | "script": "41046a0765b5865641ce08dd39690aade26dfbf5511430ca428a3089261361cef170e3929a68aee3d8d4848b0c5111b0a37b82b86ad559fd2a745b44d8e8d9dfdc0cac", 179 | "value": 100000000 180 | }, 181 | { 182 | "script": "41044a656f065871a353f216ca26cef8dde2f03e8c16202d2e8ad769f02032cb86a5eb5e56842e92e19141d60a01928f8dd2c875a390f67c1f6c94cfc617c0ea45afac", 183 | "value": 2400000000 184 | } 185 | ], 186 | "expected": [0, 1] 187 | } 188 | ] 189 | } 190 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # This file is generated by running "yarn install" inside your project. 2 | # Manual changes might be lost - proceed with caution! 3 | 4 | __metadata: 5 | version: 6 6 | cacheKey: 8 7 | 8 | "@gar/promisify@npm:^1.1.3": 9 | version: 1.1.3 10 | resolution: "@gar/promisify@npm:1.1.3" 11 | checksum: 4059f790e2d07bf3c3ff3e0fec0daa8144fe35c1f6e0111c9921bd32106adaa97a4ab096ad7dab1e28ee6a9060083c4d1a4ada42a7f5f3f7a96b8812e2b757c1 12 | languageName: node 13 | linkType: hard 14 | 15 | "@nodelib/fs.scandir@npm:2.1.5": 16 | version: 2.1.5 17 | resolution: "@nodelib/fs.scandir@npm:2.1.5" 18 | dependencies: 19 | "@nodelib/fs.stat": 2.0.5 20 | run-parallel: ^1.1.9 21 | checksum: a970d595bd23c66c880e0ef1817791432dbb7acbb8d44b7e7d0e7a22f4521260d4a83f7f9fd61d44fda4610105577f8f58a60718105fb38352baed612fd79e59 22 | languageName: node 23 | linkType: hard 24 | 25 | "@nodelib/fs.stat@npm:2.0.5, @nodelib/fs.stat@npm:^2.0.2": 26 | version: 2.0.5 27 | resolution: "@nodelib/fs.stat@npm:2.0.5" 28 | checksum: 012480b5ca9d97bff9261571dbbec7bbc6033f69cc92908bc1ecfad0792361a5a1994bc48674b9ef76419d056a03efadfce5a6cf6dbc0a36559571a7a483f6f0 29 | languageName: node 30 | linkType: hard 31 | 32 | "@nodelib/fs.walk@npm:^1.2.3": 33 | version: 1.2.8 34 | resolution: "@nodelib/fs.walk@npm:1.2.8" 35 | dependencies: 36 | "@nodelib/fs.scandir": 2.1.5 37 | fastq: ^1.6.0 38 | checksum: 190c643f156d8f8f277bf2a6078af1ffde1fd43f498f187c2db24d35b4b4b5785c02c7dc52e356497b9a1b65b13edc996de08de0b961c32844364da02986dc53 39 | languageName: node 40 | linkType: hard 41 | 42 | "@npmcli/fs@npm:^2.1.0": 43 | version: 2.1.0 44 | resolution: "@npmcli/fs@npm:2.1.0" 45 | dependencies: 46 | "@gar/promisify": ^1.1.3 47 | semver: ^7.3.5 48 | checksum: 6ec6d678af6da49f9dac50cd882d7f661934dd278972ffbaacde40d9eaa2871292d634000a0cca9510f6fc29855fbd4af433e1adbff90a524ec3eaf140f1219b 49 | languageName: node 50 | linkType: hard 51 | 52 | "@npmcli/move-file@npm:^2.0.0": 53 | version: 2.0.0 54 | resolution: "@npmcli/move-file@npm:2.0.0" 55 | dependencies: 56 | mkdirp: ^1.0.4 57 | rimraf: ^3.0.2 58 | checksum: 1388777b507b0c592d53f41b9d182e1a8de7763bc625fc07999b8edbc22325f074e5b3ec90af79c89d6987fdb2325bc66d59f483258543c14a43661621f841b0 59 | languageName: node 60 | linkType: hard 61 | 62 | "@runonbitcoin/nimble@npm:^1.0.13": 63 | version: 1.0.13 64 | resolution: "@runonbitcoin/nimble@npm:1.0.13" 65 | checksum: b4e7de91dcbb8355cd6444431710750e4f421dea28669c9cfd5accd1c368610238c3c591ce7ca8b6ffec38d552efd9ccd2fc6b1804b00587f6fe753bdf231481 66 | languageName: node 67 | linkType: hard 68 | 69 | "@tootallnate/once@npm:2": 70 | version: 2.0.0 71 | resolution: "@tootallnate/once@npm:2.0.0" 72 | checksum: ad87447820dd3f24825d2d947ebc03072b20a42bfc96cbafec16bff8bbda6c1a81fcb0be56d5b21968560c5359a0af4038a68ba150c3e1694fe4c109a063bed8 73 | languageName: node 74 | linkType: hard 75 | 76 | "abbrev@npm:1": 77 | version: 1.1.1 78 | resolution: "abbrev@npm:1.1.1" 79 | checksum: a4a97ec07d7ea112c517036882b2ac22f3109b7b19077dc656316d07d308438aac28e4d9746dc4d84bf6b1e75b4a7b0a5f3cb30592419f128ca9a8cee3bcfa17 80 | languageName: node 81 | linkType: hard 82 | 83 | "acorn-walk@npm:^8.2.0": 84 | version: 8.2.0 85 | resolution: "acorn-walk@npm:8.2.0" 86 | checksum: 1715e76c01dd7b2d4ca472f9c58968516a4899378a63ad5b6c2d668bba8da21a71976c14ec5f5b75f887b6317c4ae0b897ab141c831d741dc76024d8745f1ad1 87 | languageName: node 88 | linkType: hard 89 | 90 | "acorn@npm:^8.7.0": 91 | version: 8.7.1 92 | resolution: "acorn@npm:8.7.1" 93 | bin: 94 | acorn: bin/acorn 95 | checksum: aca0aabf98826717920ac2583fdcad0a6fbe4e583fdb6e843af2594e907455aeafe30b1e14f1757cd83ce1776773cf8296ffc3a4acf13f0bd3dfebcf1db6ae80 96 | languageName: node 97 | linkType: hard 98 | 99 | "agent-base@npm:6, agent-base@npm:^6.0.2": 100 | version: 6.0.2 101 | resolution: "agent-base@npm:6.0.2" 102 | dependencies: 103 | debug: 4 104 | checksum: f52b6872cc96fd5f622071b71ef200e01c7c4c454ee68bc9accca90c98cfb39f2810e3e9aa330435835eedc8c23f4f8a15267f67c6e245d2b33757575bdac49d 105 | languageName: node 106 | linkType: hard 107 | 108 | "agentkeepalive@npm:^4.2.1": 109 | version: 4.2.1 110 | resolution: "agentkeepalive@npm:4.2.1" 111 | dependencies: 112 | debug: ^4.1.0 113 | depd: ^1.1.2 114 | humanize-ms: ^1.2.1 115 | checksum: 39cb49ed8cf217fd6da058a92828a0a84e0b74c35550f82ee0a10e1ee403c4b78ade7948be2279b188b7a7303f5d396ea2738b134731e464bf28de00a4f72a18 116 | languageName: node 117 | linkType: hard 118 | 119 | "aggregate-error@npm:^3.0.0": 120 | version: 3.1.0 121 | resolution: "aggregate-error@npm:3.1.0" 122 | dependencies: 123 | clean-stack: ^2.0.0 124 | indent-string: ^4.0.0 125 | checksum: 1101a33f21baa27a2fa8e04b698271e64616b886795fd43c31068c07533c7b3facfcaf4e9e0cab3624bd88f729a592f1c901a1a229c9e490eafce411a8644b79 126 | languageName: node 127 | linkType: hard 128 | 129 | "aggregate-error@npm:^4.0.0": 130 | version: 4.0.0 131 | resolution: "aggregate-error@npm:4.0.0" 132 | dependencies: 133 | clean-stack: ^4.0.0 134 | indent-string: ^5.0.0 135 | checksum: 586397769e25fc5c2da5995c736f11ba83adf0bbc5f72c7101ea38e795458fd7b497f672318119218b4d3b1f8b8d3001417cebe9de55b5467af5cbcbff4befa3 136 | languageName: node 137 | linkType: hard 138 | 139 | "ansi-regex@npm:^5.0.1": 140 | version: 5.0.1 141 | resolution: "ansi-regex@npm:5.0.1" 142 | checksum: 2aa4bb54caf2d622f1afdad09441695af2a83aa3fe8b8afa581d205e57ed4261c183c4d3877cee25794443fde5876417d859c108078ab788d6af7e4fe52eb66b 143 | languageName: node 144 | linkType: hard 145 | 146 | "ansi-regex@npm:^6.0.1": 147 | version: 6.0.1 148 | resolution: "ansi-regex@npm:6.0.1" 149 | checksum: 1ff8b7667cded1de4fa2c9ae283e979fc87036864317da86a2e546725f96406746411d0d85e87a2d12fa5abd715d90006de7fa4fa0477c92321ad3b4c7d4e169 150 | languageName: node 151 | linkType: hard 152 | 153 | "ansi-styles@npm:^4.0.0": 154 | version: 4.3.0 155 | resolution: "ansi-styles@npm:4.3.0" 156 | dependencies: 157 | color-convert: ^2.0.1 158 | checksum: 513b44c3b2105dd14cc42a19271e80f386466c4be574bccf60b627432f9198571ebf4ab1e4c3ba17347658f4ee1711c163d574248c0c1cdc2d5917a0ad582ec4 159 | languageName: node 160 | linkType: hard 161 | 162 | "ansi-styles@npm:^6.0.0, ansi-styles@npm:^6.1.0": 163 | version: 6.1.0 164 | resolution: "ansi-styles@npm:6.1.0" 165 | checksum: 7a7f8528c07a9d20c3a92bccd2b6bc3bb4d26e5cb775c02826921477377bd495d615d61f710d56216344b6238d1d11ef2b0348e146c5b128715578bfb3217229 166 | languageName: node 167 | linkType: hard 168 | 169 | "anymatch@npm:~3.1.2": 170 | version: 3.1.2 171 | resolution: "anymatch@npm:3.1.2" 172 | dependencies: 173 | normalize-path: ^3.0.0 174 | picomatch: ^2.0.4 175 | checksum: 985163db2292fac9e5a1e072bf99f1b5baccf196e4de25a0b0b81865ebddeb3b3eb4480734ef0a2ac8c002845396b91aa89121f5b84f93981a4658164a9ec6e9 176 | languageName: node 177 | linkType: hard 178 | 179 | "aproba@npm:^1.0.3 || ^2.0.0": 180 | version: 2.0.0 181 | resolution: "aproba@npm:2.0.0" 182 | checksum: 5615cadcfb45289eea63f8afd064ab656006361020e1735112e346593856f87435e02d8dcc7ff0d11928bc7d425f27bc7c2a84f6c0b35ab0ff659c814c138a24 183 | languageName: node 184 | linkType: hard 185 | 186 | "are-we-there-yet@npm:^3.0.0": 187 | version: 3.0.0 188 | resolution: "are-we-there-yet@npm:3.0.0" 189 | dependencies: 190 | delegates: ^1.0.0 191 | readable-stream: ^3.6.0 192 | checksum: 348edfdd931b0b50868b55402c01c3f64df1d4c229ab6f063539a5025fd6c5f5bb8a0cab409bbed8d75d34762d22aa91b7c20b4204eb8177063158d9ba792981 193 | languageName: node 194 | linkType: hard 195 | 196 | "argparse@npm:^1.0.7": 197 | version: 1.0.10 198 | resolution: "argparse@npm:1.0.10" 199 | dependencies: 200 | sprintf-js: ~1.0.2 201 | checksum: 7ca6e45583a28de7258e39e13d81e925cfa25d7d4aacbf806a382d3c02fcb13403a07fb8aeef949f10a7cfe4a62da0e2e807b348a5980554cc28ee573ef95945 202 | languageName: node 203 | linkType: hard 204 | 205 | "array-find-index@npm:^1.0.1": 206 | version: 1.0.2 207 | resolution: "array-find-index@npm:1.0.2" 208 | checksum: aac128bf369e1ac6c06ff0bb330788371c0e256f71279fb92d745e26fb4b9db8920e485b4ec25e841c93146bf71a34dcdbcefa115e7e0f96927a214d237b7081 209 | languageName: node 210 | linkType: hard 211 | 212 | "array-union@npm:^2.1.0": 213 | version: 2.1.0 214 | resolution: "array-union@npm:2.1.0" 215 | checksum: 5bee12395cba82da674931df6d0fea23c4aa4660cb3b338ced9f828782a65caa232573e6bf3968f23e0c5eb301764a382cef2f128b170a9dc59de0e36c39f98d 216 | languageName: node 217 | linkType: hard 218 | 219 | "arrgv@npm:^1.0.2": 220 | version: 1.0.2 221 | resolution: "arrgv@npm:1.0.2" 222 | checksum: 470bbb406ea3b34810dd8b03c0b33282617a42d9fce0ab45d58596efefd042fc548eda49161fa8e3f607cbe9df90e7a67003a09043ab9081eff70f97c63dd0e2 223 | languageName: node 224 | linkType: hard 225 | 226 | "arrify@npm:^3.0.0": 227 | version: 3.0.0 228 | resolution: "arrify@npm:3.0.0" 229 | checksum: d6c6f3dad9571234f320e130d57fddb2cc283c87f2ac7df6c7005dffc5161b7bb9376f4be655ed257050330336e84afc4f3020d77696ad231ff580a94ae5aba6 230 | languageName: node 231 | linkType: hard 232 | 233 | "ava@npm:^4.2.0": 234 | version: 4.2.0 235 | resolution: "ava@npm:4.2.0" 236 | dependencies: 237 | acorn: ^8.7.0 238 | acorn-walk: ^8.2.0 239 | ansi-styles: ^6.1.0 240 | arrgv: ^1.0.2 241 | arrify: ^3.0.0 242 | callsites: ^4.0.0 243 | cbor: ^8.1.0 244 | chalk: ^5.0.0 245 | chokidar: ^3.5.3 246 | chunkd: ^2.0.1 247 | ci-info: ^3.3.0 248 | ci-parallel-vars: ^1.0.1 249 | clean-yaml-object: ^0.1.0 250 | cli-truncate: ^3.1.0 251 | code-excerpt: ^4.0.0 252 | common-path-prefix: ^3.0.0 253 | concordance: ^5.0.4 254 | currently-unhandled: ^0.4.1 255 | debug: ^4.3.3 256 | del: ^6.0.0 257 | emittery: ^0.10.1 258 | figures: ^4.0.0 259 | globby: ^13.1.1 260 | ignore-by-default: ^2.0.0 261 | indent-string: ^5.0.0 262 | is-error: ^2.2.2 263 | is-plain-object: ^5.0.0 264 | is-promise: ^4.0.0 265 | matcher: ^5.0.0 266 | mem: ^9.0.2 267 | ms: ^2.1.3 268 | p-event: ^5.0.1 269 | p-map: ^5.3.0 270 | picomatch: ^2.3.1 271 | pkg-conf: ^4.0.0 272 | plur: ^5.1.0 273 | pretty-ms: ^7.0.1 274 | resolve-cwd: ^3.0.0 275 | slash: ^3.0.0 276 | stack-utils: ^2.0.5 277 | strip-ansi: ^7.0.1 278 | supertap: ^3.0.1 279 | temp-dir: ^2.0.0 280 | write-file-atomic: ^4.0.1 281 | yargs: ^17.3.1 282 | peerDependencies: 283 | "@ava/typescript": "*" 284 | peerDependenciesMeta: 285 | "@ava/typescript": 286 | optional: true 287 | bin: 288 | ava: entrypoints/cli.mjs 289 | checksum: dc43f8b775f93f6fe2332de83e0051cdfef63014921fe79a57fc3c2b586d6443981091f7c193131a25b378655449fce9f5080460360a09d258f1697e3bfbb04d 290 | languageName: node 291 | linkType: hard 292 | 293 | "balanced-match@npm:^1.0.0": 294 | version: 1.0.2 295 | resolution: "balanced-match@npm:1.0.2" 296 | checksum: 9706c088a283058a8a99e0bf91b0a2f75497f185980d9ffa8b304de1d9e58ebda7c72c07ebf01dadedaac5b2907b2c6f566f660d62bd336c3468e960403b9d65 297 | languageName: node 298 | linkType: hard 299 | 300 | "binary-extensions@npm:^2.0.0": 301 | version: 2.2.0 302 | resolution: "binary-extensions@npm:2.2.0" 303 | checksum: ccd267956c58d2315f5d3ea6757cf09863c5fc703e50fbeb13a7dc849b812ef76e3cf9ca8f35a0c48498776a7478d7b4a0418e1e2b8cb9cb9731f2922aaad7f8 304 | languageName: node 305 | linkType: hard 306 | 307 | "blueimp-md5@npm:^2.10.0": 308 | version: 2.19.0 309 | resolution: "blueimp-md5@npm:2.19.0" 310 | checksum: 28095dcbd2c67152a2938006e8d7c74c3406ba6556071298f872505432feb2c13241b0476644160ee0a5220383ba94cb8ccdac0053b51f68d168728f9c382530 311 | languageName: node 312 | linkType: hard 313 | 314 | "brace-expansion@npm:^1.1.7": 315 | version: 1.1.11 316 | resolution: "brace-expansion@npm:1.1.11" 317 | dependencies: 318 | balanced-match: ^1.0.0 319 | concat-map: 0.0.1 320 | checksum: faf34a7bb0c3fcf4b59c7808bc5d2a96a40988addf2e7e09dfbb67a2251800e0d14cd2bfc1aa79174f2f5095c54ff27f46fb1289fe2d77dac755b5eb3434cc07 321 | languageName: node 322 | linkType: hard 323 | 324 | "brace-expansion@npm:^2.0.1": 325 | version: 2.0.1 326 | resolution: "brace-expansion@npm:2.0.1" 327 | dependencies: 328 | balanced-match: ^1.0.0 329 | checksum: a61e7cd2e8a8505e9f0036b3b6108ba5e926b4b55089eeb5550cd04a471fe216c96d4fe7e4c7f995c728c554ae20ddfc4244cad10aef255e72b62930afd233d1 330 | languageName: node 331 | linkType: hard 332 | 333 | "braces@npm:^3.0.2, braces@npm:~3.0.2": 334 | version: 3.0.2 335 | resolution: "braces@npm:3.0.2" 336 | dependencies: 337 | fill-range: ^7.0.1 338 | checksum: e2a8e769a863f3d4ee887b5fe21f63193a891c68b612ddb4b68d82d1b5f3ff9073af066c343e9867a393fe4c2555dcb33e89b937195feb9c1613d259edfcd459 339 | languageName: node 340 | linkType: hard 341 | 342 | "cacache@npm:^16.0.2": 343 | version: 16.0.7 344 | resolution: "cacache@npm:16.0.7" 345 | dependencies: 346 | "@npmcli/fs": ^2.1.0 347 | "@npmcli/move-file": ^2.0.0 348 | chownr: ^2.0.0 349 | fs-minipass: ^2.1.0 350 | glob: ^8.0.1 351 | infer-owner: ^1.0.4 352 | lru-cache: ^7.7.1 353 | minipass: ^3.1.6 354 | minipass-collect: ^1.0.2 355 | minipass-flush: ^1.0.5 356 | minipass-pipeline: ^1.2.4 357 | mkdirp: ^1.0.4 358 | p-map: ^4.0.0 359 | promise-inflight: ^1.0.1 360 | rimraf: ^3.0.2 361 | ssri: ^9.0.0 362 | tar: ^6.1.11 363 | unique-filename: ^1.1.1 364 | checksum: 2155b099b7e0f0369fb1155ca4673532ca7efe2ebdbec63acca8743580b8446b5d4fd7184626b1cb059001af77b981cdc67035c7855544d365d4f048eafca2ca 365 | languageName: node 366 | linkType: hard 367 | 368 | "callsites@npm:^4.0.0": 369 | version: 4.0.0 370 | resolution: "callsites@npm:4.0.0" 371 | checksum: ad3c3a57328a539c0d671cf1ca500abf09461b762807fc545a132026bdf87705fee9c299e1adb38b133c29201a3b04fbf4f2b90d8fa1d9e00ef507e803737cf2 372 | languageName: node 373 | linkType: hard 374 | 375 | "cbor@npm:^8.1.0": 376 | version: 8.1.0 377 | resolution: "cbor@npm:8.1.0" 378 | dependencies: 379 | nofilter: ^3.1.0 380 | checksum: a90338435dc7b45cc01461af979e3bb6ddd4f2a08584c437586039cd5f2235014c06e49d664295debbfb3514d87b2f06728092ab6aa6175e2e85e9cd7dc0c1fd 381 | languageName: node 382 | linkType: hard 383 | 384 | "chalk@npm:^5.0.0": 385 | version: 5.0.1 386 | resolution: "chalk@npm:5.0.1" 387 | checksum: 7b45300372b908f0471fbf7389ce2f5de8d85bb949026fd51a1b95b10d0ed32c7ed5aab36dd5e9d2bf3191867909b4404cef75c5f4d2d1daeeacd301dd280b76 388 | languageName: node 389 | linkType: hard 390 | 391 | "chokidar@npm:^3.5.3": 392 | version: 3.5.3 393 | resolution: "chokidar@npm:3.5.3" 394 | dependencies: 395 | anymatch: ~3.1.2 396 | braces: ~3.0.2 397 | fsevents: ~2.3.2 398 | glob-parent: ~5.1.2 399 | is-binary-path: ~2.1.0 400 | is-glob: ~4.0.1 401 | normalize-path: ~3.0.0 402 | readdirp: ~3.6.0 403 | dependenciesMeta: 404 | fsevents: 405 | optional: true 406 | checksum: b49fcde40176ba007ff361b198a2d35df60d9bb2a5aab228279eb810feae9294a6b4649ab15981304447afe1e6ffbf4788ad5db77235dc770ab777c6e771980c 407 | languageName: node 408 | linkType: hard 409 | 410 | "chownr@npm:^2.0.0": 411 | version: 2.0.0 412 | resolution: "chownr@npm:2.0.0" 413 | checksum: c57cf9dd0791e2f18a5ee9c1a299ae6e801ff58fee96dc8bfd0dcb4738a6ce58dd252a3605b1c93c6418fe4f9d5093b28ffbf4d66648cb2a9c67eaef9679be2f 414 | languageName: node 415 | linkType: hard 416 | 417 | "chunkd@npm:^2.0.1": 418 | version: 2.0.1 419 | resolution: "chunkd@npm:2.0.1" 420 | checksum: bab8cc08c752a3648984385dc6f61d751e89dbeef648d22a3b661e1d470eaa0f5182f0b4303710f13ae83d2f85144f8eb2dde7a975861d9021b5c56b881f457b 421 | languageName: node 422 | linkType: hard 423 | 424 | "ci-info@npm:^3.3.0": 425 | version: 3.3.0 426 | resolution: "ci-info@npm:3.3.0" 427 | checksum: c3d86fe374938ecda5093b1ba39acb535d8309185ba3f23587747c6a057e63f45419b406d880304dbc0e1d72392c9a33e42fe9a1e299209bc0ded5efaa232b66 428 | languageName: node 429 | linkType: hard 430 | 431 | "ci-parallel-vars@npm:^1.0.1": 432 | version: 1.0.1 433 | resolution: "ci-parallel-vars@npm:1.0.1" 434 | checksum: ae859831f7e8e3585db731b8306c336616e37bd709dad1d7775ea4c0731aefd94741dabb48201edc6827d000008fd7fb72cb977967614ee2d99d6b499f0c35fe 435 | languageName: node 436 | linkType: hard 437 | 438 | "clean-stack@npm:^2.0.0": 439 | version: 2.2.0 440 | resolution: "clean-stack@npm:2.2.0" 441 | checksum: 2ac8cd2b2f5ec986a3c743935ec85b07bc174d5421a5efc8017e1f146a1cf5f781ae962618f416352103b32c9cd7e203276e8c28241bbe946160cab16149fb68 442 | languageName: node 443 | linkType: hard 444 | 445 | "clean-stack@npm:^4.0.0": 446 | version: 4.1.0 447 | resolution: "clean-stack@npm:4.1.0" 448 | dependencies: 449 | escape-string-regexp: 5.0.0 450 | checksum: 56fdca7a67a61528676eaddce35442824700b674cf882150ac416824966f65b5e15d4ea02d1d47f4d16ad50213718c1f00ae0f6cca46aeec3fd761b49cf7204f 451 | languageName: node 452 | linkType: hard 453 | 454 | "clean-yaml-object@npm:^0.1.0": 455 | version: 0.1.0 456 | resolution: "clean-yaml-object@npm:0.1.0" 457 | checksum: 0374ad2f1fbd4984ecf56ebc62200092f6372b9ccf1b7971bb979c328fb12fe76e759fb1e8adc491c80b7b1861f9f00c7f19813dd2a0f49c88231422c70451f4 458 | languageName: node 459 | linkType: hard 460 | 461 | "cli-truncate@npm:^3.1.0": 462 | version: 3.1.0 463 | resolution: "cli-truncate@npm:3.1.0" 464 | dependencies: 465 | slice-ansi: ^5.0.0 466 | string-width: ^5.0.0 467 | checksum: c3243e41974445691c63f8b405df1d5a24049dc33d324fe448dc572e561a7b772ae982692900b1a5960901cc4fc7def25a629b9c69a4208ee89d12ab3332617a 468 | languageName: node 469 | linkType: hard 470 | 471 | "cliui@npm:^7.0.2": 472 | version: 7.0.4 473 | resolution: "cliui@npm:7.0.4" 474 | dependencies: 475 | string-width: ^4.2.0 476 | strip-ansi: ^6.0.0 477 | wrap-ansi: ^7.0.0 478 | checksum: ce2e8f578a4813806788ac399b9e866297740eecd4ad1823c27fd344d78b22c5f8597d548adbcc46f0573e43e21e751f39446c5a5e804a12aace402b7a315d7f 479 | languageName: node 480 | linkType: hard 481 | 482 | "code-excerpt@npm:^4.0.0": 483 | version: 4.0.0 484 | resolution: "code-excerpt@npm:4.0.0" 485 | dependencies: 486 | convert-to-spaces: ^2.0.1 487 | checksum: d57137d8f4825879283a828cc02a1115b56858dc54ed06c625c8f67d6685d1becd2fbaa7f0ab19ecca1f5cca03f8c97bbc1f013cab40261e4d3275032e65efe9 488 | languageName: node 489 | linkType: hard 490 | 491 | "color-convert@npm:^2.0.1": 492 | version: 2.0.1 493 | resolution: "color-convert@npm:2.0.1" 494 | dependencies: 495 | color-name: ~1.1.4 496 | checksum: 79e6bdb9fd479a205c71d89574fccfb22bd9053bd98c6c4d870d65c132e5e904e6034978e55b43d69fcaa7433af2016ee203ce76eeba9cfa554b373e7f7db336 497 | languageName: node 498 | linkType: hard 499 | 500 | "color-name@npm:~1.1.4": 501 | version: 1.1.4 502 | resolution: "color-name@npm:1.1.4" 503 | checksum: b0445859521eb4021cd0fb0cc1a75cecf67fceecae89b63f62b201cca8d345baf8b952c966862a9d9a2632987d4f6581f0ec8d957dfacece86f0a7919316f610 504 | languageName: node 505 | linkType: hard 506 | 507 | "color-support@npm:^1.1.3": 508 | version: 1.1.3 509 | resolution: "color-support@npm:1.1.3" 510 | bin: 511 | color-support: bin.js 512 | checksum: 9b7356817670b9a13a26ca5af1c21615463b500783b739b7634a0c2047c16cef4b2865d7576875c31c3cddf9dd621fa19285e628f20198b233a5cfdda6d0793b 513 | languageName: node 514 | linkType: hard 515 | 516 | "common-path-prefix@npm:^3.0.0": 517 | version: 3.0.0 518 | resolution: "common-path-prefix@npm:3.0.0" 519 | checksum: fdb3c4f54e51e70d417ccd950c07f757582de800c0678ca388aedefefc84982039f346f9fd9a1252d08d2da9e9ef4019f580a1d1d3a10da031e4bb3c924c5818 520 | languageName: node 521 | linkType: hard 522 | 523 | "concat-map@npm:0.0.1": 524 | version: 0.0.1 525 | resolution: "concat-map@npm:0.0.1" 526 | checksum: 902a9f5d8967a3e2faf138d5cb784b9979bad2e6db5357c5b21c568df4ebe62bcb15108af1b2253744844eb964fc023fbd9afbbbb6ddd0bcc204c6fb5b7bf3af 527 | languageName: node 528 | linkType: hard 529 | 530 | "concordance@npm:^5.0.4": 531 | version: 5.0.4 532 | resolution: "concordance@npm:5.0.4" 533 | dependencies: 534 | date-time: ^3.1.0 535 | esutils: ^2.0.3 536 | fast-diff: ^1.2.0 537 | js-string-escape: ^1.0.1 538 | lodash: ^4.17.15 539 | md5-hex: ^3.0.1 540 | semver: ^7.3.2 541 | well-known-symbols: ^2.0.0 542 | checksum: 749153ba711492feb7c3d2f5bb04c107157440b3e39509bd5dd19ee7b3ac751d1e4cd75796d9f702e0a713312dbc661421c68aa4a2c34d5f6d91f47e3a1c64a6 543 | languageName: node 544 | linkType: hard 545 | 546 | "console-control-strings@npm:^1.1.0": 547 | version: 1.1.0 548 | resolution: "console-control-strings@npm:1.1.0" 549 | checksum: 8755d76787f94e6cf79ce4666f0c5519906d7f5b02d4b884cf41e11dcd759ed69c57da0670afd9236d229a46e0f9cf519db0cd829c6dca820bb5a5c3def584ed 550 | languageName: node 551 | linkType: hard 552 | 553 | "convert-to-spaces@npm:^2.0.1": 554 | version: 2.0.1 555 | resolution: "convert-to-spaces@npm:2.0.1" 556 | checksum: bbb324e5916fe9866f65c0ff5f9c1ea933764d0bdb09fccaf59542e40545ed483db6b2339c6d9eb56a11965a58f1a6038f3174f0e2fb7601343c7107ca5e2751 557 | languageName: node 558 | linkType: hard 559 | 560 | "currently-unhandled@npm:^0.4.1": 561 | version: 0.4.1 562 | resolution: "currently-unhandled@npm:0.4.1" 563 | dependencies: 564 | array-find-index: ^1.0.1 565 | checksum: 1f59fe10b5339b54b1a1eee110022f663f3495cf7cf2f480686e89edc7fa8bfe42dbab4b54f85034bc8b092a76cc7becbc2dad4f9adad332ab5831bec39ad540 566 | languageName: node 567 | linkType: hard 568 | 569 | "date-time@npm:^3.1.0": 570 | version: 3.1.0 571 | resolution: "date-time@npm:3.1.0" 572 | dependencies: 573 | time-zone: ^1.0.0 574 | checksum: f9cfcd1b15dfeabab15c0b9d18eb9e4e2d9d4371713564178d46a8f91ad577a290b5178b80050718d02d9c0cf646f8a875011e12d1ed05871e9f72c72c8a8fe6 575 | languageName: node 576 | linkType: hard 577 | 578 | "debug@npm:4, debug@npm:^4.1.0, debug@npm:^4.3.3": 579 | version: 4.3.4 580 | resolution: "debug@npm:4.3.4" 581 | dependencies: 582 | ms: 2.1.2 583 | peerDependenciesMeta: 584 | supports-color: 585 | optional: true 586 | checksum: 3dbad3f94ea64f34431a9cbf0bafb61853eda57bff2880036153438f50fb5a84f27683ba0d8e5426bf41a8c6ff03879488120cf5b3a761e77953169c0600a708 587 | languageName: node 588 | linkType: hard 589 | 590 | "del@npm:^6.0.0": 591 | version: 6.0.0 592 | resolution: "del@npm:6.0.0" 593 | dependencies: 594 | globby: ^11.0.1 595 | graceful-fs: ^4.2.4 596 | is-glob: ^4.0.1 597 | is-path-cwd: ^2.2.0 598 | is-path-inside: ^3.0.2 599 | p-map: ^4.0.0 600 | rimraf: ^3.0.2 601 | slash: ^3.0.0 602 | checksum: 5742891627e91aaf62385714025233f4664da28bc55b6ab825649dcdea4691fed3cf329a2b1913fd2d2612e693e99e08a03c84cac7f36ef54bacac9390520192 603 | languageName: node 604 | linkType: hard 605 | 606 | "delegates@npm:^1.0.0": 607 | version: 1.0.0 608 | resolution: "delegates@npm:1.0.0" 609 | checksum: a51744d9b53c164ba9c0492471a1a2ffa0b6727451bdc89e31627fdf4adda9d51277cfcbfb20f0a6f08ccb3c436f341df3e92631a3440226d93a8971724771fd 610 | languageName: node 611 | linkType: hard 612 | 613 | "depd@npm:^1.1.2": 614 | version: 1.1.2 615 | resolution: "depd@npm:1.1.2" 616 | checksum: 6b406620d269619852885ce15965272b829df6f409724415e0002c8632ab6a8c0a08ec1f0bd2add05dc7bd7507606f7e2cc034fa24224ab829580040b835ecd9 617 | languageName: node 618 | linkType: hard 619 | 620 | "dir-glob@npm:^3.0.1": 621 | version: 3.0.1 622 | resolution: "dir-glob@npm:3.0.1" 623 | dependencies: 624 | path-type: ^4.0.0 625 | checksum: fa05e18324510d7283f55862f3161c6759a3f2f8dbce491a2fc14c8324c498286c54282c1f0e933cb930da8419b30679389499b919122952a4f8592362ef4615 626 | languageName: node 627 | linkType: hard 628 | 629 | "eastasianwidth@npm:^0.2.0": 630 | version: 0.2.0 631 | resolution: "eastasianwidth@npm:0.2.0" 632 | checksum: 7d00d7cd8e49b9afa762a813faac332dee781932d6f2c848dc348939c4253f1d4564341b7af1d041853bc3f32c2ef141b58e0a4d9862c17a7f08f68df1e0f1ed 633 | languageName: node 634 | linkType: hard 635 | 636 | "emittery@npm:^0.10.1": 637 | version: 0.10.2 638 | resolution: "emittery@npm:0.10.2" 639 | checksum: ee3e21788b043b90885b18ea756ec3105c1cedc50b29709c92b01e239c7e55345d4bb6d3aef4ddbaf528eef448a40b3bb831bad9ee0fc9c25cbf1367ab1ab5ac 640 | languageName: node 641 | linkType: hard 642 | 643 | "emoji-regex@npm:^8.0.0": 644 | version: 8.0.0 645 | resolution: "emoji-regex@npm:8.0.0" 646 | checksum: d4c5c39d5a9868b5fa152f00cada8a936868fd3367f33f71be515ecee4c803132d11b31a6222b2571b1e5f7e13890156a94880345594d0ce7e3c9895f560f192 647 | languageName: node 648 | linkType: hard 649 | 650 | "emoji-regex@npm:^9.2.2": 651 | version: 9.2.2 652 | resolution: "emoji-regex@npm:9.2.2" 653 | checksum: 8487182da74aabd810ac6d6f1994111dfc0e331b01271ae01ec1eb0ad7b5ecc2bbbbd2f053c05cb55a1ac30449527d819bbfbf0e3de1023db308cbcb47f86601 654 | languageName: node 655 | linkType: hard 656 | 657 | "encoding@npm:^0.1.13": 658 | version: 0.1.13 659 | resolution: "encoding@npm:0.1.13" 660 | dependencies: 661 | iconv-lite: ^0.6.2 662 | checksum: bb98632f8ffa823996e508ce6a58ffcf5856330fde839ae42c9e1f436cc3b5cc651d4aeae72222916545428e54fd0f6aa8862fd8d25bdbcc4589f1e3f3715e7f 663 | languageName: node 664 | linkType: hard 665 | 666 | "env-paths@npm:^2.2.0": 667 | version: 2.2.1 668 | resolution: "env-paths@npm:2.2.1" 669 | checksum: 65b5df55a8bab92229ab2b40dad3b387fad24613263d103a97f91c9fe43ceb21965cd3392b1ccb5d77088021e525c4e0481adb309625d0cb94ade1d1fb8dc17e 670 | languageName: node 671 | linkType: hard 672 | 673 | "err-code@npm:^2.0.2": 674 | version: 2.0.3 675 | resolution: "err-code@npm:2.0.3" 676 | checksum: 8b7b1be20d2de12d2255c0bc2ca638b7af5171142693299416e6a9339bd7d88fc8d7707d913d78e0993176005405a236b066b45666b27b797252c771156ace54 677 | languageName: node 678 | linkType: hard 679 | 680 | "esbuild-android-64@npm:0.14.38": 681 | version: 0.14.38 682 | resolution: "esbuild-android-64@npm:0.14.38" 683 | conditions: os=android & cpu=x64 684 | languageName: node 685 | linkType: hard 686 | 687 | "esbuild-android-arm64@npm:0.14.38": 688 | version: 0.14.38 689 | resolution: "esbuild-android-arm64@npm:0.14.38" 690 | conditions: os=android & cpu=arm64 691 | languageName: node 692 | linkType: hard 693 | 694 | "esbuild-darwin-64@npm:0.14.38": 695 | version: 0.14.38 696 | resolution: "esbuild-darwin-64@npm:0.14.38" 697 | conditions: os=darwin & cpu=x64 698 | languageName: node 699 | linkType: hard 700 | 701 | "esbuild-darwin-arm64@npm:0.14.38": 702 | version: 0.14.38 703 | resolution: "esbuild-darwin-arm64@npm:0.14.38" 704 | conditions: os=darwin & cpu=arm64 705 | languageName: node 706 | linkType: hard 707 | 708 | "esbuild-freebsd-64@npm:0.14.38": 709 | version: 0.14.38 710 | resolution: "esbuild-freebsd-64@npm:0.14.38" 711 | conditions: os=freebsd & cpu=x64 712 | languageName: node 713 | linkType: hard 714 | 715 | "esbuild-freebsd-arm64@npm:0.14.38": 716 | version: 0.14.38 717 | resolution: "esbuild-freebsd-arm64@npm:0.14.38" 718 | conditions: os=freebsd & cpu=arm64 719 | languageName: node 720 | linkType: hard 721 | 722 | "esbuild-linux-32@npm:0.14.38": 723 | version: 0.14.38 724 | resolution: "esbuild-linux-32@npm:0.14.38" 725 | conditions: os=linux & cpu=ia32 726 | languageName: node 727 | linkType: hard 728 | 729 | "esbuild-linux-64@npm:0.14.38": 730 | version: 0.14.38 731 | resolution: "esbuild-linux-64@npm:0.14.38" 732 | conditions: os=linux & cpu=x64 733 | languageName: node 734 | linkType: hard 735 | 736 | "esbuild-linux-arm64@npm:0.14.38": 737 | version: 0.14.38 738 | resolution: "esbuild-linux-arm64@npm:0.14.38" 739 | conditions: os=linux & cpu=arm64 740 | languageName: node 741 | linkType: hard 742 | 743 | "esbuild-linux-arm@npm:0.14.38": 744 | version: 0.14.38 745 | resolution: "esbuild-linux-arm@npm:0.14.38" 746 | conditions: os=linux & cpu=arm 747 | languageName: node 748 | linkType: hard 749 | 750 | "esbuild-linux-mips64le@npm:0.14.38": 751 | version: 0.14.38 752 | resolution: "esbuild-linux-mips64le@npm:0.14.38" 753 | conditions: os=linux & cpu=mips64el 754 | languageName: node 755 | linkType: hard 756 | 757 | "esbuild-linux-ppc64le@npm:0.14.38": 758 | version: 0.14.38 759 | resolution: "esbuild-linux-ppc64le@npm:0.14.38" 760 | conditions: os=linux & cpu=ppc64 761 | languageName: node 762 | linkType: hard 763 | 764 | "esbuild-linux-riscv64@npm:0.14.38": 765 | version: 0.14.38 766 | resolution: "esbuild-linux-riscv64@npm:0.14.38" 767 | conditions: os=linux & cpu=riscv64 768 | languageName: node 769 | linkType: hard 770 | 771 | "esbuild-linux-s390x@npm:0.14.38": 772 | version: 0.14.38 773 | resolution: "esbuild-linux-s390x@npm:0.14.38" 774 | conditions: os=linux & cpu=s390x 775 | languageName: node 776 | linkType: hard 777 | 778 | "esbuild-netbsd-64@npm:0.14.38": 779 | version: 0.14.38 780 | resolution: "esbuild-netbsd-64@npm:0.14.38" 781 | conditions: os=netbsd & cpu=x64 782 | languageName: node 783 | linkType: hard 784 | 785 | "esbuild-openbsd-64@npm:0.14.38": 786 | version: 0.14.38 787 | resolution: "esbuild-openbsd-64@npm:0.14.38" 788 | conditions: os=openbsd & cpu=x64 789 | languageName: node 790 | linkType: hard 791 | 792 | "esbuild-plugin-globals@npm:^0.1.1": 793 | version: 0.1.1 794 | resolution: "esbuild-plugin-globals@npm:0.1.1" 795 | checksum: 07460a455174e280829e172096b70422a945bcaba0efbe2e71a88ccdcc194e0251c7a7cdc27fd5dda3420777dc6f7ee6312e1e3167af2627a67a39be7d7c373d 796 | languageName: node 797 | linkType: hard 798 | 799 | "esbuild-sunos-64@npm:0.14.38": 800 | version: 0.14.38 801 | resolution: "esbuild-sunos-64@npm:0.14.38" 802 | conditions: os=sunos & cpu=x64 803 | languageName: node 804 | linkType: hard 805 | 806 | "esbuild-windows-32@npm:0.14.38": 807 | version: 0.14.38 808 | resolution: "esbuild-windows-32@npm:0.14.38" 809 | conditions: os=win32 & cpu=ia32 810 | languageName: node 811 | linkType: hard 812 | 813 | "esbuild-windows-64@npm:0.14.38": 814 | version: 0.14.38 815 | resolution: "esbuild-windows-64@npm:0.14.38" 816 | conditions: os=win32 & cpu=x64 817 | languageName: node 818 | linkType: hard 819 | 820 | "esbuild-windows-arm64@npm:0.14.38": 821 | version: 0.14.38 822 | resolution: "esbuild-windows-arm64@npm:0.14.38" 823 | conditions: os=win32 & cpu=arm64 824 | languageName: node 825 | linkType: hard 826 | 827 | "esbuild@npm:^0.14.38": 828 | version: 0.14.38 829 | resolution: "esbuild@npm:0.14.38" 830 | dependencies: 831 | esbuild-android-64: 0.14.38 832 | esbuild-android-arm64: 0.14.38 833 | esbuild-darwin-64: 0.14.38 834 | esbuild-darwin-arm64: 0.14.38 835 | esbuild-freebsd-64: 0.14.38 836 | esbuild-freebsd-arm64: 0.14.38 837 | esbuild-linux-32: 0.14.38 838 | esbuild-linux-64: 0.14.38 839 | esbuild-linux-arm: 0.14.38 840 | esbuild-linux-arm64: 0.14.38 841 | esbuild-linux-mips64le: 0.14.38 842 | esbuild-linux-ppc64le: 0.14.38 843 | esbuild-linux-riscv64: 0.14.38 844 | esbuild-linux-s390x: 0.14.38 845 | esbuild-netbsd-64: 0.14.38 846 | esbuild-openbsd-64: 0.14.38 847 | esbuild-sunos-64: 0.14.38 848 | esbuild-windows-32: 0.14.38 849 | esbuild-windows-64: 0.14.38 850 | esbuild-windows-arm64: 0.14.38 851 | dependenciesMeta: 852 | esbuild-android-64: 853 | optional: true 854 | esbuild-android-arm64: 855 | optional: true 856 | esbuild-darwin-64: 857 | optional: true 858 | esbuild-darwin-arm64: 859 | optional: true 860 | esbuild-freebsd-64: 861 | optional: true 862 | esbuild-freebsd-arm64: 863 | optional: true 864 | esbuild-linux-32: 865 | optional: true 866 | esbuild-linux-64: 867 | optional: true 868 | esbuild-linux-arm: 869 | optional: true 870 | esbuild-linux-arm64: 871 | optional: true 872 | esbuild-linux-mips64le: 873 | optional: true 874 | esbuild-linux-ppc64le: 875 | optional: true 876 | esbuild-linux-riscv64: 877 | optional: true 878 | esbuild-linux-s390x: 879 | optional: true 880 | esbuild-netbsd-64: 881 | optional: true 882 | esbuild-openbsd-64: 883 | optional: true 884 | esbuild-sunos-64: 885 | optional: true 886 | esbuild-windows-32: 887 | optional: true 888 | esbuild-windows-64: 889 | optional: true 890 | esbuild-windows-arm64: 891 | optional: true 892 | bin: 893 | esbuild: bin/esbuild 894 | checksum: d7523a36bd28016c010829c527386dbc0c6b9f514920abf5ac8003f346665161aa61026fd6822c5091fc1c1af52fe26c9281a81740fc06f2994cdbb7c2880297 895 | languageName: node 896 | linkType: hard 897 | 898 | "escalade@npm:^3.1.1": 899 | version: 3.1.1 900 | resolution: "escalade@npm:3.1.1" 901 | checksum: a3e2a99f07acb74b3ad4989c48ca0c3140f69f923e56d0cba0526240ee470b91010f9d39001f2a4a313841d237ede70a729e92125191ba5d21e74b106800b133 902 | languageName: node 903 | linkType: hard 904 | 905 | "escape-string-regexp@npm:5.0.0, escape-string-regexp@npm:^5.0.0": 906 | version: 5.0.0 907 | resolution: "escape-string-regexp@npm:5.0.0" 908 | checksum: 20daabe197f3cb198ec28546deebcf24b3dbb1a5a269184381b3116d12f0532e06007f4bc8da25669d6a7f8efb68db0758df4cd981f57bc5b57f521a3e12c59e 909 | languageName: node 910 | linkType: hard 911 | 912 | "escape-string-regexp@npm:^2.0.0": 913 | version: 2.0.0 914 | resolution: "escape-string-regexp@npm:2.0.0" 915 | checksum: 9f8a2d5743677c16e85c810e3024d54f0c8dea6424fad3c79ef6666e81dd0846f7437f5e729dfcdac8981bc9e5294c39b4580814d114076b8d36318f46ae4395 916 | languageName: node 917 | linkType: hard 918 | 919 | "esprima@npm:^4.0.0": 920 | version: 4.0.1 921 | resolution: "esprima@npm:4.0.1" 922 | bin: 923 | esparse: ./bin/esparse.js 924 | esvalidate: ./bin/esvalidate.js 925 | checksum: b45bc805a613dbea2835278c306b91aff6173c8d034223fa81498c77dcbce3b2931bf6006db816f62eacd9fd4ea975dfd85a5b7f3c6402cfd050d4ca3c13a628 926 | languageName: node 927 | linkType: hard 928 | 929 | "esutils@npm:^2.0.3": 930 | version: 2.0.3 931 | resolution: "esutils@npm:2.0.3" 932 | checksum: 22b5b08f74737379a840b8ed2036a5fb35826c709ab000683b092d9054e5c2a82c27818f12604bfc2a9a76b90b6834ef081edbc1c7ae30d1627012e067c6ec87 933 | languageName: node 934 | linkType: hard 935 | 936 | "fast-diff@npm:^1.2.0": 937 | version: 1.2.0 938 | resolution: "fast-diff@npm:1.2.0" 939 | checksum: 1b5306eaa9e826564d9e5ffcd6ebd881eb5f770b3f977fcbf38f05c824e42172b53c79920e8429c54eb742ce15a0caf268b0fdd5b38f6de52234c4a8368131ae 940 | languageName: node 941 | linkType: hard 942 | 943 | "fast-glob@npm:^3.2.11, fast-glob@npm:^3.2.9": 944 | version: 3.2.11 945 | resolution: "fast-glob@npm:3.2.11" 946 | dependencies: 947 | "@nodelib/fs.stat": ^2.0.2 948 | "@nodelib/fs.walk": ^1.2.3 949 | glob-parent: ^5.1.2 950 | merge2: ^1.3.0 951 | micromatch: ^4.0.4 952 | checksum: f473105324a7780a20c06de842e15ddbb41d3cb7e71d1e4fe6e8373204f22245d54f5ab9e2061e6a1c613047345954d29b022e0e76f5c28b1df9858179a0e6d7 953 | languageName: node 954 | linkType: hard 955 | 956 | "fastq@npm:^1.6.0": 957 | version: 1.13.0 958 | resolution: "fastq@npm:1.13.0" 959 | dependencies: 960 | reusify: ^1.0.4 961 | checksum: 32cf15c29afe622af187d12fc9cd93e160a0cb7c31a3bb6ace86b7dea3b28e7b72acde89c882663f307b2184e14782c6c664fa315973c03626c7d4bff070bb0b 962 | languageName: node 963 | linkType: hard 964 | 965 | "figures@npm:^4.0.0": 966 | version: 4.0.1 967 | resolution: "figures@npm:4.0.1" 968 | dependencies: 969 | escape-string-regexp: ^5.0.0 970 | is-unicode-supported: ^1.2.0 971 | checksum: 08564c70ec6be8dbd26e24e4f35bacb8d9beb729b3b7faa9cd7ad54f5232b7f9a39f788a847ec45677664d568c86323001d1042482d089c0d0f311e197ad1148 972 | languageName: node 973 | linkType: hard 974 | 975 | "fill-range@npm:^7.0.1": 976 | version: 7.0.1 977 | resolution: "fill-range@npm:7.0.1" 978 | dependencies: 979 | to-regex-range: ^5.0.1 980 | checksum: cc283f4e65b504259e64fd969bcf4def4eb08d85565e906b7d36516e87819db52029a76b6363d0f02d0d532f0033c9603b9e2d943d56ee3b0d4f7ad3328ff917 981 | languageName: node 982 | linkType: hard 983 | 984 | "find-up@npm:^6.0.0": 985 | version: 6.3.0 986 | resolution: "find-up@npm:6.3.0" 987 | dependencies: 988 | locate-path: ^7.1.0 989 | path-exists: ^5.0.0 990 | checksum: 9a21b7f9244a420e54c6df95b4f6fc3941efd3c3e5476f8274eb452f6a85706e7a6a90de71353ee4f091fcb4593271a6f92810a324ec542650398f928783c280 991 | languageName: node 992 | linkType: hard 993 | 994 | "fs-minipass@npm:^2.0.0, fs-minipass@npm:^2.1.0": 995 | version: 2.1.0 996 | resolution: "fs-minipass@npm:2.1.0" 997 | dependencies: 998 | minipass: ^3.0.0 999 | checksum: 1b8d128dae2ac6cc94230cc5ead341ba3e0efaef82dab46a33d171c044caaa6ca001364178d42069b2809c35a1c3c35079a32107c770e9ffab3901b59af8c8b1 1000 | languageName: node 1001 | linkType: hard 1002 | 1003 | "fs.realpath@npm:^1.0.0": 1004 | version: 1.0.0 1005 | resolution: "fs.realpath@npm:1.0.0" 1006 | checksum: 99ddea01a7e75aa276c250a04eedeffe5662bce66c65c07164ad6264f9de18fb21be9433ead460e54cff20e31721c811f4fb5d70591799df5f85dce6d6746fd0 1007 | languageName: node 1008 | linkType: hard 1009 | 1010 | "fsevents@npm:~2.3.2": 1011 | version: 2.3.2 1012 | resolution: "fsevents@npm:2.3.2" 1013 | dependencies: 1014 | node-gyp: latest 1015 | checksum: 97ade64e75091afee5265e6956cb72ba34db7819b4c3e94c431d4be2b19b8bb7a2d4116da417950c3425f17c8fe693d25e20212cac583ac1521ad066b77ae31f 1016 | conditions: os=darwin 1017 | languageName: node 1018 | linkType: hard 1019 | 1020 | "fsevents@patch:fsevents@~2.3.2#~builtin": 1021 | version: 2.3.2 1022 | resolution: "fsevents@patch:fsevents@npm%3A2.3.2#~builtin::version=2.3.2&hash=18f3a7" 1023 | dependencies: 1024 | node-gyp: latest 1025 | conditions: os=darwin 1026 | languageName: node 1027 | linkType: hard 1028 | 1029 | "gauge@npm:^4.0.3": 1030 | version: 4.0.4 1031 | resolution: "gauge@npm:4.0.4" 1032 | dependencies: 1033 | aproba: ^1.0.3 || ^2.0.0 1034 | color-support: ^1.1.3 1035 | console-control-strings: ^1.1.0 1036 | has-unicode: ^2.0.1 1037 | signal-exit: ^3.0.7 1038 | string-width: ^4.2.3 1039 | strip-ansi: ^6.0.1 1040 | wide-align: ^1.1.5 1041 | checksum: 788b6bfe52f1dd8e263cda800c26ac0ca2ff6de0b6eee2fe0d9e3abf15e149b651bd27bf5226be10e6e3edb5c4e5d5985a5a1a98137e7a892f75eff76467ad2d 1042 | languageName: node 1043 | linkType: hard 1044 | 1045 | "get-caller-file@npm:^2.0.5": 1046 | version: 2.0.5 1047 | resolution: "get-caller-file@npm:2.0.5" 1048 | checksum: b9769a836d2a98c3ee734a88ba712e62703f1df31b94b784762c433c27a386dd6029ff55c2a920c392e33657d80191edbf18c61487e198844844516f843496b9 1049 | languageName: node 1050 | linkType: hard 1051 | 1052 | "glob-parent@npm:^5.1.2, glob-parent@npm:~5.1.2": 1053 | version: 5.1.2 1054 | resolution: "glob-parent@npm:5.1.2" 1055 | dependencies: 1056 | is-glob: ^4.0.1 1057 | checksum: f4f2bfe2425296e8a47e36864e4f42be38a996db40420fe434565e4480e3322f18eb37589617a98640c5dc8fdec1a387007ee18dbb1f3f5553409c34d17f425e 1058 | languageName: node 1059 | linkType: hard 1060 | 1061 | "glob@npm:^7.1.3, glob@npm:^7.1.4": 1062 | version: 7.2.0 1063 | resolution: "glob@npm:7.2.0" 1064 | dependencies: 1065 | fs.realpath: ^1.0.0 1066 | inflight: ^1.0.4 1067 | inherits: 2 1068 | minimatch: ^3.0.4 1069 | once: ^1.3.0 1070 | path-is-absolute: ^1.0.0 1071 | checksum: 78a8ea942331f08ed2e055cb5b9e40fe6f46f579d7fd3d694f3412fe5db23223d29b7fee1575440202e9a7ff9a72ab106a39fee39934c7bedafe5e5f8ae20134 1072 | languageName: node 1073 | linkType: hard 1074 | 1075 | "glob@npm:^8.0.1": 1076 | version: 8.0.1 1077 | resolution: "glob@npm:8.0.1" 1078 | dependencies: 1079 | fs.realpath: ^1.0.0 1080 | inflight: ^1.0.4 1081 | inherits: 2 1082 | minimatch: ^5.0.1 1083 | once: ^1.3.0 1084 | path-is-absolute: ^1.0.0 1085 | checksum: 7ac782f3ef1c08005884447479e68ceb0ad56997eb2003e1e9aefae71bad3cb48eb7c49190d3d6736f2ffcd8af4985d53a46831b3d5e0052cc5756822a38b61a 1086 | languageName: node 1087 | linkType: hard 1088 | 1089 | "globby@npm:^11.0.1": 1090 | version: 11.1.0 1091 | resolution: "globby@npm:11.1.0" 1092 | dependencies: 1093 | array-union: ^2.1.0 1094 | dir-glob: ^3.0.1 1095 | fast-glob: ^3.2.9 1096 | ignore: ^5.2.0 1097 | merge2: ^1.4.1 1098 | slash: ^3.0.0 1099 | checksum: b4be8885e0cfa018fc783792942d53926c35c50b3aefd3fdcfb9d22c627639dc26bd2327a40a0b74b074100ce95bb7187bfeae2f236856aa3de183af7a02aea6 1100 | languageName: node 1101 | linkType: hard 1102 | 1103 | "globby@npm:^13.1.1": 1104 | version: 13.1.1 1105 | resolution: "globby@npm:13.1.1" 1106 | dependencies: 1107 | dir-glob: ^3.0.1 1108 | fast-glob: ^3.2.11 1109 | ignore: ^5.2.0 1110 | merge2: ^1.4.1 1111 | slash: ^4.0.0 1112 | checksum: e6c43409c6c31b374fbd1c01a8c1811de52336928be9c697e472d2a89a156c9cbf1fb33863755c0447b4db16485858aa57f16628d66a6b7c7131669c9fbe76cd 1113 | languageName: node 1114 | linkType: hard 1115 | 1116 | "graceful-fs@npm:^4.2.4, graceful-fs@npm:^4.2.6": 1117 | version: 4.2.10 1118 | resolution: "graceful-fs@npm:4.2.10" 1119 | checksum: 3f109d70ae123951905d85032ebeae3c2a5a7a997430df00ea30df0e3a6c60cf6689b109654d6fdacd28810a053348c4d14642da1d075049e6be1ba5216218da 1120 | languageName: node 1121 | linkType: hard 1122 | 1123 | "has-unicode@npm:^2.0.1": 1124 | version: 2.0.1 1125 | resolution: "has-unicode@npm:2.0.1" 1126 | checksum: 1eab07a7436512db0be40a710b29b5dc21fa04880b7f63c9980b706683127e3c1b57cb80ea96d47991bdae2dfe479604f6a1ba410106ee1046a41d1bd0814400 1127 | languageName: node 1128 | linkType: hard 1129 | 1130 | "http-cache-semantics@npm:^4.1.0": 1131 | version: 4.1.0 1132 | resolution: "http-cache-semantics@npm:4.1.0" 1133 | checksum: 974de94a81c5474be07f269f9fd8383e92ebb5a448208223bfb39e172a9dbc26feff250192ecc23b9593b3f92098e010406b0f24bd4d588d631f80214648ed42 1134 | languageName: node 1135 | linkType: hard 1136 | 1137 | "http-proxy-agent@npm:^5.0.0": 1138 | version: 5.0.0 1139 | resolution: "http-proxy-agent@npm:5.0.0" 1140 | dependencies: 1141 | "@tootallnate/once": 2 1142 | agent-base: 6 1143 | debug: 4 1144 | checksum: e2ee1ff1656a131953839b2a19cd1f3a52d97c25ba87bd2559af6ae87114abf60971e498021f9b73f9fd78aea8876d1fb0d4656aac8a03c6caa9fc175f22b786 1145 | languageName: node 1146 | linkType: hard 1147 | 1148 | "https-proxy-agent@npm:^5.0.0": 1149 | version: 5.0.1 1150 | resolution: "https-proxy-agent@npm:5.0.1" 1151 | dependencies: 1152 | agent-base: 6 1153 | debug: 4 1154 | checksum: 571fccdf38184f05943e12d37d6ce38197becdd69e58d03f43637f7fa1269cf303a7d228aa27e5b27bbd3af8f09fd938e1c91dcfefff2df7ba77c20ed8dfc765 1155 | languageName: node 1156 | linkType: hard 1157 | 1158 | "humanize-ms@npm:^1.2.1": 1159 | version: 1.2.1 1160 | resolution: "humanize-ms@npm:1.2.1" 1161 | dependencies: 1162 | ms: ^2.0.0 1163 | checksum: 9c7a74a2827f9294c009266c82031030eae811ca87b0da3dceb8d6071b9bde22c9f3daef0469c3c533cc67a97d8a167cd9fc0389350e5f415f61a79b171ded16 1164 | languageName: node 1165 | linkType: hard 1166 | 1167 | "iconv-lite@npm:^0.6.2": 1168 | version: 0.6.3 1169 | resolution: "iconv-lite@npm:0.6.3" 1170 | dependencies: 1171 | safer-buffer: ">= 2.1.2 < 3.0.0" 1172 | checksum: 3f60d47a5c8fc3313317edfd29a00a692cc87a19cac0159e2ce711d0ebc9019064108323b5e493625e25594f11c6236647d8e256fbe7a58f4a3b33b89e6d30bf 1173 | languageName: node 1174 | linkType: hard 1175 | 1176 | "ignore-by-default@npm:^2.0.0": 1177 | version: 2.0.0 1178 | resolution: "ignore-by-default@npm:2.0.0" 1179 | checksum: c9934ea2ad751ded6016c4155cdd72ad5e7c6220cb1113ee080c6ac86f72ab0c6abbebbd53bf678595c616eccf0974d6275dd5818a64301f75d602ba1d15f5bb 1180 | languageName: node 1181 | linkType: hard 1182 | 1183 | "ignore@npm:^5.2.0": 1184 | version: 5.2.0 1185 | resolution: "ignore@npm:5.2.0" 1186 | checksum: 6b1f926792d614f64c6c83da3a1f9c83f6196c2839aa41e1e32dd7b8d174cef2e329d75caabb62cb61ce9dc432f75e67d07d122a037312db7caa73166a1bdb77 1187 | languageName: node 1188 | linkType: hard 1189 | 1190 | "imurmurhash@npm:^0.1.4": 1191 | version: 0.1.4 1192 | resolution: "imurmurhash@npm:0.1.4" 1193 | checksum: 7cae75c8cd9a50f57dadd77482359f659eaebac0319dd9368bcd1714f55e65badd6929ca58569da2b6494ef13fdd5598cd700b1eba23f8b79c5f19d195a3ecf7 1194 | languageName: node 1195 | linkType: hard 1196 | 1197 | "indent-string@npm:^4.0.0": 1198 | version: 4.0.0 1199 | resolution: "indent-string@npm:4.0.0" 1200 | checksum: 824cfb9929d031dabf059bebfe08cf3137365e112019086ed3dcff6a0a7b698cb80cf67ccccde0e25b9e2d7527aa6cc1fed1ac490c752162496caba3e6699612 1201 | languageName: node 1202 | linkType: hard 1203 | 1204 | "indent-string@npm:^5.0.0": 1205 | version: 5.0.0 1206 | resolution: "indent-string@npm:5.0.0" 1207 | checksum: e466c27b6373440e6d84fbc19e750219ce25865cb82d578e41a6053d727e5520dc5725217d6eb1cc76005a1bb1696a0f106d84ce7ebda3033b963a38583fb3b3 1208 | languageName: node 1209 | linkType: hard 1210 | 1211 | "infer-owner@npm:^1.0.4": 1212 | version: 1.0.4 1213 | resolution: "infer-owner@npm:1.0.4" 1214 | checksum: 181e732764e4a0611576466b4b87dac338972b839920b2a8cde43642e4ed6bd54dc1fb0b40874728f2a2df9a1b097b8ff83b56d5f8f8e3927f837fdcb47d8a89 1215 | languageName: node 1216 | linkType: hard 1217 | 1218 | "inflight@npm:^1.0.4": 1219 | version: 1.0.6 1220 | resolution: "inflight@npm:1.0.6" 1221 | dependencies: 1222 | once: ^1.3.0 1223 | wrappy: 1 1224 | checksum: f4f76aa072ce19fae87ce1ef7d221e709afb59d445e05d47fba710e85470923a75de35bfae47da6de1b18afc3ce83d70facf44cfb0aff89f0a3f45c0a0244dfd 1225 | languageName: node 1226 | linkType: hard 1227 | 1228 | "inherits@npm:2, inherits@npm:^2.0.3": 1229 | version: 2.0.4 1230 | resolution: "inherits@npm:2.0.4" 1231 | checksum: 4a48a733847879d6cf6691860a6b1e3f0f4754176e4d71494c41f3475553768b10f84b5ce1d40fbd0e34e6bfbb864ee35858ad4dd2cf31e02fc4a154b724d7f1 1232 | languageName: node 1233 | linkType: hard 1234 | 1235 | "ip@npm:^1.1.5": 1236 | version: 1.1.5 1237 | resolution: "ip@npm:1.1.5" 1238 | checksum: 30133981f082a060a32644f6a7746e9ba7ac9e2bc07ecc8bbdda3ee8ca9bec1190724c390e45a1ee7695e7edfd2a8f7dda2c104ec5f7ac5068c00648504c7e5a 1239 | languageName: node 1240 | linkType: hard 1241 | 1242 | "irregular-plurals@npm:^3.3.0": 1243 | version: 3.3.0 1244 | resolution: "irregular-plurals@npm:3.3.0" 1245 | checksum: 1282d8adfb00a9718655ce21e5b096d4b31d2115c817a30e1e3254648ae4ac0f84d706cd0cad2a93c64f4bb5c5572ea8f63fcc9766f005a5362031c56d9e77b5 1246 | languageName: node 1247 | linkType: hard 1248 | 1249 | "is-binary-path@npm:~2.1.0": 1250 | version: 2.1.0 1251 | resolution: "is-binary-path@npm:2.1.0" 1252 | dependencies: 1253 | binary-extensions: ^2.0.0 1254 | checksum: 84192eb88cff70d320426f35ecd63c3d6d495da9d805b19bc65b518984b7c0760280e57dbf119b7e9be6b161784a5a673ab2c6abe83abb5198a432232ad5b35c 1255 | languageName: node 1256 | linkType: hard 1257 | 1258 | "is-error@npm:^2.2.2": 1259 | version: 2.2.2 1260 | resolution: "is-error@npm:2.2.2" 1261 | checksum: a97b39587150f0d38f9f93f64699807fe3020fe5edbd63548f234dc2ba96fd7c776d66c062bf031dfeb93c7f48db563ff6bde588418ca041da37c659a416f055 1262 | languageName: node 1263 | linkType: hard 1264 | 1265 | "is-extglob@npm:^2.1.1": 1266 | version: 2.1.1 1267 | resolution: "is-extglob@npm:2.1.1" 1268 | checksum: df033653d06d0eb567461e58a7a8c9f940bd8c22274b94bf7671ab36df5719791aae15eef6d83bbb5e23283967f2f984b8914559d4449efda578c775c4be6f85 1269 | languageName: node 1270 | linkType: hard 1271 | 1272 | "is-fullwidth-code-point@npm:^3.0.0": 1273 | version: 3.0.0 1274 | resolution: "is-fullwidth-code-point@npm:3.0.0" 1275 | checksum: 44a30c29457c7fb8f00297bce733f0a64cd22eca270f83e58c105e0d015e45c019491a4ab2faef91ab51d4738c670daff901c799f6a700e27f7314029e99e348 1276 | languageName: node 1277 | linkType: hard 1278 | 1279 | "is-fullwidth-code-point@npm:^4.0.0": 1280 | version: 4.0.0 1281 | resolution: "is-fullwidth-code-point@npm:4.0.0" 1282 | checksum: 8ae89bf5057bdf4f57b346fb6c55e9c3dd2549983d54191d722d5c739397a903012cc41a04ee3403fd872e811243ef91a7c5196da7b5841dc6b6aae31a264a8d 1283 | languageName: node 1284 | linkType: hard 1285 | 1286 | "is-glob@npm:^4.0.1, is-glob@npm:~4.0.1": 1287 | version: 4.0.3 1288 | resolution: "is-glob@npm:4.0.3" 1289 | dependencies: 1290 | is-extglob: ^2.1.1 1291 | checksum: d381c1319fcb69d341cc6e6c7cd588e17cd94722d9a32dbd60660b993c4fb7d0f19438674e68dfec686d09b7c73139c9166b47597f846af387450224a8101ab4 1292 | languageName: node 1293 | linkType: hard 1294 | 1295 | "is-lambda@npm:^1.0.1": 1296 | version: 1.0.1 1297 | resolution: "is-lambda@npm:1.0.1" 1298 | checksum: 93a32f01940220532e5948538699ad610d5924ac86093fcee83022252b363eb0cc99ba53ab084a04e4fb62bf7b5731f55496257a4c38adf87af9c4d352c71c35 1299 | languageName: node 1300 | linkType: hard 1301 | 1302 | "is-number@npm:^7.0.0": 1303 | version: 7.0.0 1304 | resolution: "is-number@npm:7.0.0" 1305 | checksum: 456ac6f8e0f3111ed34668a624e45315201dff921e5ac181f8ec24923b99e9f32ca1a194912dc79d539c97d33dba17dc635202ff0b2cf98326f608323276d27a 1306 | languageName: node 1307 | linkType: hard 1308 | 1309 | "is-path-cwd@npm:^2.2.0": 1310 | version: 2.2.0 1311 | resolution: "is-path-cwd@npm:2.2.0" 1312 | checksum: 46a840921bb8cc0dc7b5b423a14220e7db338072a4495743a8230533ce78812dc152548c86f4b828411fe98c5451959f07cf841c6a19f611e46600bd699e8048 1313 | languageName: node 1314 | linkType: hard 1315 | 1316 | "is-path-inside@npm:^3.0.2": 1317 | version: 3.0.3 1318 | resolution: "is-path-inside@npm:3.0.3" 1319 | checksum: abd50f06186a052b349c15e55b182326f1936c89a78bf6c8f2b707412517c097ce04bc49a0ca221787bc44e1049f51f09a2ffb63d22899051988d3a618ba13e9 1320 | languageName: node 1321 | linkType: hard 1322 | 1323 | "is-plain-object@npm:^5.0.0": 1324 | version: 5.0.0 1325 | resolution: "is-plain-object@npm:5.0.0" 1326 | checksum: e32d27061eef62c0847d303125440a38660517e586f2f3db7c9d179ae5b6674ab0f469d519b2e25c147a1a3bc87156d0d5f4d8821e0ce4a9ee7fe1fcf11ce45c 1327 | languageName: node 1328 | linkType: hard 1329 | 1330 | "is-promise@npm:^4.0.0": 1331 | version: 4.0.0 1332 | resolution: "is-promise@npm:4.0.0" 1333 | checksum: 0b46517ad47b00b6358fd6553c83ec1f6ba9acd7ffb3d30a0bf519c5c69e7147c132430452351b8a9fc198f8dd6c4f76f8e6f5a7f100f8c77d57d9e0f4261a8a 1334 | languageName: node 1335 | linkType: hard 1336 | 1337 | "is-unicode-supported@npm:^1.2.0": 1338 | version: 1.2.0 1339 | resolution: "is-unicode-supported@npm:1.2.0" 1340 | checksum: 2d90b4b3ce622c1ecf7414b8954cc8f0483576d4d8e6892cbbdc1e2dd33d6126b1cf0319cf1549bee03d45f989b8b0de3309c879a9388a4fe6b8836f866ed86c 1341 | languageName: node 1342 | linkType: hard 1343 | 1344 | "isexe@npm:^2.0.0": 1345 | version: 2.0.0 1346 | resolution: "isexe@npm:2.0.0" 1347 | checksum: 26bf6c5480dda5161c820c5b5c751ae1e766c587b1f951ea3fcfc973bafb7831ae5b54a31a69bd670220e42e99ec154475025a468eae58ea262f813fdc8d1c62 1348 | languageName: node 1349 | linkType: hard 1350 | 1351 | "js-string-escape@npm:^1.0.1": 1352 | version: 1.0.1 1353 | resolution: "js-string-escape@npm:1.0.1" 1354 | checksum: f11e0991bf57e0c183b55c547acec85bd2445f043efc9ea5aa68b41bd2a3e7d3ce94636cb233ae0d84064ba4c1a505d32e969813c5b13f81e7d4be12c59256fe 1355 | languageName: node 1356 | linkType: hard 1357 | 1358 | "js-yaml@npm:^3.14.1": 1359 | version: 3.14.1 1360 | resolution: "js-yaml@npm:3.14.1" 1361 | dependencies: 1362 | argparse: ^1.0.7 1363 | esprima: ^4.0.0 1364 | bin: 1365 | js-yaml: bin/js-yaml.js 1366 | checksum: bef146085f472d44dee30ec34e5cf36bf89164f5d585435a3d3da89e52622dff0b188a580e4ad091c3341889e14cb88cac6e4deb16dc5b1e9623bb0601fc255c 1367 | languageName: node 1368 | linkType: hard 1369 | 1370 | "load-json-file@npm:^7.0.0": 1371 | version: 7.0.1 1372 | resolution: "load-json-file@npm:7.0.1" 1373 | checksum: a560288da6891778321ef993e4bdbdf05374a4f3a3aeedd5ba6b64672798c830d748cfc59a2ec9891a3db30e78b3d04172e0dcb0d4828168289a393147ca0e74 1374 | languageName: node 1375 | linkType: hard 1376 | 1377 | "locate-path@npm:^7.1.0": 1378 | version: 7.1.0 1379 | resolution: "locate-path@npm:7.1.0" 1380 | dependencies: 1381 | p-locate: ^6.0.0 1382 | checksum: 17d5eb6c04ff31856f8a6ae4ee3e3091d41485657428d1a91bd5f66aa1fcd7a90db3de6e8ffb905c2ff1a0014b77509b98dd6410424505efc08b1726d50bcbfc 1383 | languageName: node 1384 | linkType: hard 1385 | 1386 | "lodash@npm:^4.17.15": 1387 | version: 4.17.21 1388 | resolution: "lodash@npm:4.17.21" 1389 | checksum: eb835a2e51d381e561e508ce932ea50a8e5a68f4ebdd771ea240d3048244a8d13658acbd502cd4829768c56f2e16bdd4340b9ea141297d472517b83868e677f7 1390 | languageName: node 1391 | linkType: hard 1392 | 1393 | "lru-cache@npm:^6.0.0": 1394 | version: 6.0.0 1395 | resolution: "lru-cache@npm:6.0.0" 1396 | dependencies: 1397 | yallist: ^4.0.0 1398 | checksum: f97f499f898f23e4585742138a22f22526254fdba6d75d41a1c2526b3b6cc5747ef59c5612ba7375f42aca4f8461950e925ba08c991ead0651b4918b7c978297 1399 | languageName: node 1400 | linkType: hard 1401 | 1402 | "lru-cache@npm:^7.7.1": 1403 | version: 7.8.1 1404 | resolution: "lru-cache@npm:7.8.1" 1405 | checksum: 31ea67388c9774300331d70f4affd5a433869bcf0fae5405f967d19d7b447930b713b0566a2e95362c9082034a8b496f3671ccf8f0c061d8e8048412663f9432 1406 | languageName: node 1407 | linkType: hard 1408 | 1409 | "make-fetch-happen@npm:^10.0.3": 1410 | version: 10.1.2 1411 | resolution: "make-fetch-happen@npm:10.1.2" 1412 | dependencies: 1413 | agentkeepalive: ^4.2.1 1414 | cacache: ^16.0.2 1415 | http-cache-semantics: ^4.1.0 1416 | http-proxy-agent: ^5.0.0 1417 | https-proxy-agent: ^5.0.0 1418 | is-lambda: ^1.0.1 1419 | lru-cache: ^7.7.1 1420 | minipass: ^3.1.6 1421 | minipass-collect: ^1.0.2 1422 | minipass-fetch: ^2.0.3 1423 | minipass-flush: ^1.0.5 1424 | minipass-pipeline: ^1.2.4 1425 | negotiator: ^0.6.3 1426 | promise-retry: ^2.0.1 1427 | socks-proxy-agent: ^6.1.1 1428 | ssri: ^9.0.0 1429 | checksum: 42825d119a7e4f5b1a8e7048a86d328cd36bb1ff875d155ce7079d9a0afdd310c198fb310096af358cfa9ecdf643cecf960380686792457dccb36e17efe89eb0 1430 | languageName: node 1431 | linkType: hard 1432 | 1433 | "map-age-cleaner@npm:^0.1.3": 1434 | version: 0.1.3 1435 | resolution: "map-age-cleaner@npm:0.1.3" 1436 | dependencies: 1437 | p-defer: ^1.0.0 1438 | checksum: cb2804a5bcb3cbdfe4b59066ea6d19f5e7c8c196cd55795ea4c28f792b192e4c442426ae52524e5e1acbccf393d3bddacefc3d41f803e66453f6c4eda3650bc1 1439 | languageName: node 1440 | linkType: hard 1441 | 1442 | "matcher@npm:^5.0.0": 1443 | version: 5.0.0 1444 | resolution: "matcher@npm:5.0.0" 1445 | dependencies: 1446 | escape-string-regexp: ^5.0.0 1447 | checksum: 28f191c2d23fee0f6f32fd0181d9fe173b0ab815a919edba55605438a2f9fa40372e002574a1b17add981b0a8669c75bc6194318d065ed2dceffd8b160c38118 1448 | languageName: node 1449 | linkType: hard 1450 | 1451 | "md5-hex@npm:^3.0.1": 1452 | version: 3.0.1 1453 | resolution: "md5-hex@npm:3.0.1" 1454 | dependencies: 1455 | blueimp-md5: ^2.10.0 1456 | checksum: 6799a19e8bdd3e0c2861b94c1d4d858a89220488d7885c1fa236797e367d0c2e5f2b789e05309307083503f85be3603a9686a5915568a473137d6b4117419cc2 1457 | languageName: node 1458 | linkType: hard 1459 | 1460 | "mem@npm:^9.0.2": 1461 | version: 9.0.2 1462 | resolution: "mem@npm:9.0.2" 1463 | dependencies: 1464 | map-age-cleaner: ^0.1.3 1465 | mimic-fn: ^4.0.0 1466 | checksum: 07829bb182af0e3ecf748dc2edb1c3b10a256ef10458f7e24d06561a2adc2b3ef34d14abe81678bbcedb46faa477e7370223f118b1a5e1252da5fe43496f3967 1467 | languageName: node 1468 | linkType: hard 1469 | 1470 | "merge2@npm:^1.3.0, merge2@npm:^1.4.1": 1471 | version: 1.4.1 1472 | resolution: "merge2@npm:1.4.1" 1473 | checksum: 7268db63ed5169466540b6fb947aec313200bcf6d40c5ab722c22e242f651994619bcd85601602972d3c85bd2cc45a358a4c61937e9f11a061919a1da569b0c2 1474 | languageName: node 1475 | linkType: hard 1476 | 1477 | "micromatch@npm:^4.0.4": 1478 | version: 4.0.5 1479 | resolution: "micromatch@npm:4.0.5" 1480 | dependencies: 1481 | braces: ^3.0.2 1482 | picomatch: ^2.3.1 1483 | checksum: 02a17b671c06e8fefeeb6ef996119c1e597c942e632a21ef589154f23898c9c6a9858526246abb14f8bca6e77734aa9dcf65476fca47cedfb80d9577d52843fc 1484 | languageName: node 1485 | linkType: hard 1486 | 1487 | "mimic-fn@npm:^4.0.0": 1488 | version: 4.0.0 1489 | resolution: "mimic-fn@npm:4.0.0" 1490 | checksum: 995dcece15ee29aa16e188de6633d43a3db4611bcf93620e7e62109ec41c79c0f34277165b8ce5e361205049766e371851264c21ac64ca35499acb5421c2ba56 1491 | languageName: node 1492 | linkType: hard 1493 | 1494 | "minimatch@npm:^3.0.4": 1495 | version: 3.1.2 1496 | resolution: "minimatch@npm:3.1.2" 1497 | dependencies: 1498 | brace-expansion: ^1.1.7 1499 | checksum: c154e566406683e7bcb746e000b84d74465b3a832c45d59912b9b55cd50dee66e5c4b1e5566dba26154040e51672f9aa450a9aef0c97cfc7336b78b7afb9540a 1500 | languageName: node 1501 | linkType: hard 1502 | 1503 | "minimatch@npm:^5.0.1": 1504 | version: 5.0.1 1505 | resolution: "minimatch@npm:5.0.1" 1506 | dependencies: 1507 | brace-expansion: ^2.0.1 1508 | checksum: b34b98463da4754bc526b244d680c69d4d6089451ebe512edaf6dd9eeed0279399cfa3edb19233513b8f830bf4bfcad911dddcdf125e75074100d52f724774f0 1509 | languageName: node 1510 | linkType: hard 1511 | 1512 | "minipass-collect@npm:^1.0.2": 1513 | version: 1.0.2 1514 | resolution: "minipass-collect@npm:1.0.2" 1515 | dependencies: 1516 | minipass: ^3.0.0 1517 | checksum: 14df761028f3e47293aee72888f2657695ec66bd7d09cae7ad558da30415fdc4752bbfee66287dcc6fd5e6a2fa3466d6c484dc1cbd986525d9393b9523d97f10 1518 | languageName: node 1519 | linkType: hard 1520 | 1521 | "minipass-fetch@npm:^2.0.3": 1522 | version: 2.1.0 1523 | resolution: "minipass-fetch@npm:2.1.0" 1524 | dependencies: 1525 | encoding: ^0.1.13 1526 | minipass: ^3.1.6 1527 | minipass-sized: ^1.0.3 1528 | minizlib: ^2.1.2 1529 | dependenciesMeta: 1530 | encoding: 1531 | optional: true 1532 | checksum: 1334732859a3f7959ed22589bafd9c40384b885aebb5932328071c33f86b3eb181d54c86919675d1825ab5f1c8e4f328878c863873258d113c29d79a4b0c9c9f 1533 | languageName: node 1534 | linkType: hard 1535 | 1536 | "minipass-flush@npm:^1.0.5": 1537 | version: 1.0.5 1538 | resolution: "minipass-flush@npm:1.0.5" 1539 | dependencies: 1540 | minipass: ^3.0.0 1541 | checksum: 56269a0b22bad756a08a94b1ffc36b7c9c5de0735a4dd1ab2b06c066d795cfd1f0ac44a0fcae13eece5589b908ecddc867f04c745c7009be0b566421ea0944cf 1542 | languageName: node 1543 | linkType: hard 1544 | 1545 | "minipass-pipeline@npm:^1.2.4": 1546 | version: 1.2.4 1547 | resolution: "minipass-pipeline@npm:1.2.4" 1548 | dependencies: 1549 | minipass: ^3.0.0 1550 | checksum: b14240dac0d29823c3d5911c286069e36d0b81173d7bdf07a7e4a91ecdef92cdff4baaf31ea3746f1c61e0957f652e641223970870e2353593f382112257971b 1551 | languageName: node 1552 | linkType: hard 1553 | 1554 | "minipass-sized@npm:^1.0.3": 1555 | version: 1.0.3 1556 | resolution: "minipass-sized@npm:1.0.3" 1557 | dependencies: 1558 | minipass: ^3.0.0 1559 | checksum: 79076749fcacf21b5d16dd596d32c3b6bf4d6e62abb43868fac21674078505c8b15eaca4e47ed844985a4514854f917d78f588fcd029693709417d8f98b2bd60 1560 | languageName: node 1561 | linkType: hard 1562 | 1563 | "minipass@npm:^3.0.0, minipass@npm:^3.1.1, minipass@npm:^3.1.6": 1564 | version: 3.1.6 1565 | resolution: "minipass@npm:3.1.6" 1566 | dependencies: 1567 | yallist: ^4.0.0 1568 | checksum: 57a04041413a3531a65062452cb5175f93383ef245d6f4a2961d34386eb9aa8ac11ac7f16f791f5e8bbaf1dfb1ef01596870c88e8822215db57aa591a5bb0a77 1569 | languageName: node 1570 | linkType: hard 1571 | 1572 | "minizlib@npm:^2.1.1, minizlib@npm:^2.1.2": 1573 | version: 2.1.2 1574 | resolution: "minizlib@npm:2.1.2" 1575 | dependencies: 1576 | minipass: ^3.0.0 1577 | yallist: ^4.0.0 1578 | checksum: f1fdeac0b07cf8f30fcf12f4b586795b97be856edea22b5e9072707be51fc95d41487faec3f265b42973a304fe3a64acd91a44a3826a963e37b37bafde0212c3 1579 | languageName: node 1580 | linkType: hard 1581 | 1582 | "mkdirp@npm:^1.0.3, mkdirp@npm:^1.0.4": 1583 | version: 1.0.4 1584 | resolution: "mkdirp@npm:1.0.4" 1585 | bin: 1586 | mkdirp: bin/cmd.js 1587 | checksum: a96865108c6c3b1b8e1d5e9f11843de1e077e57737602de1b82030815f311be11f96f09cce59bd5b903d0b29834733e5313f9301e3ed6d6f6fba2eae0df4298f 1588 | languageName: node 1589 | linkType: hard 1590 | 1591 | "ms@npm:2.1.2": 1592 | version: 2.1.2 1593 | resolution: "ms@npm:2.1.2" 1594 | checksum: 673cdb2c3133eb050c745908d8ce632ed2c02d85640e2edb3ace856a2266a813b30c613569bf3354fdf4ea7d1a1494add3bfa95e2713baa27d0c2c71fc44f58f 1595 | languageName: node 1596 | linkType: hard 1597 | 1598 | "ms@npm:^2.0.0, ms@npm:^2.1.3": 1599 | version: 2.1.3 1600 | resolution: "ms@npm:2.1.3" 1601 | checksum: aa92de608021b242401676e35cfa5aa42dd70cbdc082b916da7fb925c542173e36bce97ea3e804923fe92c0ad991434e4a38327e15a1b5b5f945d66df615ae6d 1602 | languageName: node 1603 | linkType: hard 1604 | 1605 | "negotiator@npm:^0.6.3": 1606 | version: 0.6.3 1607 | resolution: "negotiator@npm:0.6.3" 1608 | checksum: b8ffeb1e262eff7968fc90a2b6767b04cfd9842582a9d0ece0af7049537266e7b2506dfb1d107a32f06dd849ab2aea834d5830f7f4d0e5cb7d36e1ae55d021d9 1609 | languageName: node 1610 | linkType: hard 1611 | 1612 | "node-gyp@npm:latest": 1613 | version: 9.0.0 1614 | resolution: "node-gyp@npm:9.0.0" 1615 | dependencies: 1616 | env-paths: ^2.2.0 1617 | glob: ^7.1.4 1618 | graceful-fs: ^4.2.6 1619 | make-fetch-happen: ^10.0.3 1620 | nopt: ^5.0.0 1621 | npmlog: ^6.0.0 1622 | rimraf: ^3.0.2 1623 | semver: ^7.3.5 1624 | tar: ^6.1.2 1625 | which: ^2.0.2 1626 | bin: 1627 | node-gyp: bin/node-gyp.js 1628 | checksum: 4d8ef8860f7e4f4d86c91db3f519d26ed5cc23b48fe54543e2afd86162b4acbd14f21de42a5db344525efb69a991e021b96a68c70c6e2d5f4a5cb770793da6d3 1629 | languageName: node 1630 | linkType: hard 1631 | 1632 | "nofilter@npm:^3.1.0": 1633 | version: 3.1.0 1634 | resolution: "nofilter@npm:3.1.0" 1635 | checksum: 58aa85a5b4b35cbb6e42de8a8591c5e338061edc9f3e7286f2c335e9e9b9b8fa7c335ae45daa8a1f3433164dc0b9a3d187fa96f9516e04a17a1f9ce722becc4f 1636 | languageName: node 1637 | linkType: hard 1638 | 1639 | "nopt@npm:^5.0.0": 1640 | version: 5.0.0 1641 | resolution: "nopt@npm:5.0.0" 1642 | dependencies: 1643 | abbrev: 1 1644 | bin: 1645 | nopt: bin/nopt.js 1646 | checksum: d35fdec187269503843924e0114c0c6533fb54bbf1620d0f28b4b60ba01712d6687f62565c55cc20a504eff0fbe5c63e22340c3fad549ad40469ffb611b04f2f 1647 | languageName: node 1648 | linkType: hard 1649 | 1650 | "normalize-path@npm:^3.0.0, normalize-path@npm:~3.0.0": 1651 | version: 3.0.0 1652 | resolution: "normalize-path@npm:3.0.0" 1653 | checksum: 88eeb4da891e10b1318c4b2476b6e2ecbeb5ff97d946815ffea7794c31a89017c70d7f34b3c2ebf23ef4e9fc9fb99f7dffe36da22011b5b5c6ffa34f4873ec20 1654 | languageName: node 1655 | linkType: hard 1656 | 1657 | "npmlog@npm:^6.0.0": 1658 | version: 6.0.2 1659 | resolution: "npmlog@npm:6.0.2" 1660 | dependencies: 1661 | are-we-there-yet: ^3.0.0 1662 | console-control-strings: ^1.1.0 1663 | gauge: ^4.0.3 1664 | set-blocking: ^2.0.0 1665 | checksum: ae238cd264a1c3f22091cdd9e2b106f684297d3c184f1146984ecbe18aaa86343953f26b9520dedd1b1372bc0316905b736c1932d778dbeb1fcf5a1001390e2a 1666 | languageName: node 1667 | linkType: hard 1668 | 1669 | "once@npm:^1.3.0": 1670 | version: 1.4.0 1671 | resolution: "once@npm:1.4.0" 1672 | dependencies: 1673 | wrappy: 1 1674 | checksum: cd0a88501333edd640d95f0d2700fbde6bff20b3d4d9bdc521bdd31af0656b5706570d6c6afe532045a20bb8dc0849f8332d6f2a416e0ba6d3d3b98806c7db68 1675 | languageName: node 1676 | linkType: hard 1677 | 1678 | "p-defer@npm:^1.0.0": 1679 | version: 1.0.0 1680 | resolution: "p-defer@npm:1.0.0" 1681 | checksum: 4271b935c27987e7b6f229e5de4cdd335d808465604644cb7b4c4c95bef266735859a93b16415af8a41fd663ee9e3b97a1a2023ca9def613dba1bad2a0da0c7b 1682 | languageName: node 1683 | linkType: hard 1684 | 1685 | "p-event@npm:^5.0.1": 1686 | version: 5.0.1 1687 | resolution: "p-event@npm:5.0.1" 1688 | dependencies: 1689 | p-timeout: ^5.0.2 1690 | checksum: 3bdd8df6092e6b149f25e9c2eb1c0843b3b4279b07be2a2c72c02b65b267a8908c2040fefd606f2497b0f2bcefcd214f8ca5a74f0c883515d400ccf1d88d5683 1691 | languageName: node 1692 | linkType: hard 1693 | 1694 | "p-limit@npm:^4.0.0": 1695 | version: 4.0.0 1696 | resolution: "p-limit@npm:4.0.0" 1697 | dependencies: 1698 | yocto-queue: ^1.0.0 1699 | checksum: 01d9d70695187788f984226e16c903475ec6a947ee7b21948d6f597bed788e3112cc7ec2e171c1d37125057a5f45f3da21d8653e04a3a793589e12e9e80e756b 1700 | languageName: node 1701 | linkType: hard 1702 | 1703 | "p-locate@npm:^6.0.0": 1704 | version: 6.0.0 1705 | resolution: "p-locate@npm:6.0.0" 1706 | dependencies: 1707 | p-limit: ^4.0.0 1708 | checksum: 2bfe5234efa5e7a4e74b30a5479a193fdd9236f8f6b4d2f3f69e3d286d9a7d7ab0c118a2a50142efcf4e41625def635bd9332d6cbf9cc65d85eb0718c579ab38 1709 | languageName: node 1710 | linkType: hard 1711 | 1712 | "p-map@npm:^4.0.0": 1713 | version: 4.0.0 1714 | resolution: "p-map@npm:4.0.0" 1715 | dependencies: 1716 | aggregate-error: ^3.0.0 1717 | checksum: cb0ab21ec0f32ddffd31dfc250e3afa61e103ef43d957cc45497afe37513634589316de4eb88abdfd969fe6410c22c0b93ab24328833b8eb1ccc087fc0442a1c 1718 | languageName: node 1719 | linkType: hard 1720 | 1721 | "p-map@npm:^5.3.0": 1722 | version: 5.3.0 1723 | resolution: "p-map@npm:5.3.0" 1724 | dependencies: 1725 | aggregate-error: ^4.0.0 1726 | checksum: 744f47b110a8a4c0a302fcae901abcf4224f269239d14f5f1eb2bf790a96e68146ad1acba0b57d8aeff013d17b2145324d9b56c601284db324fa13816ee155ec 1727 | languageName: node 1728 | linkType: hard 1729 | 1730 | "p-timeout@npm:^5.0.2": 1731 | version: 5.0.2 1732 | resolution: "p-timeout@npm:5.0.2" 1733 | checksum: 69eda717e2cbc3d831617a805e069ce69f8cbf51b70f1d1760d3d118536c6b891bbc74f758a4e0d337180ff053f65cdbfc9f5dd04f368ab38173e76f47cf13a8 1734 | languageName: node 1735 | linkType: hard 1736 | 1737 | "parse-ms@npm:^2.1.0": 1738 | version: 2.1.0 1739 | resolution: "parse-ms@npm:2.1.0" 1740 | checksum: d5c66c76cca8df5bd0574e2d11b9c3752893b59b466e74308d4a2f09760dc5436a1633f549cad300fc8c3c19154d14959a3b8333d3b2f7bd75898fe18149d564 1741 | languageName: node 1742 | linkType: hard 1743 | 1744 | "path-exists@npm:^5.0.0": 1745 | version: 5.0.0 1746 | resolution: "path-exists@npm:5.0.0" 1747 | checksum: 8ca842868cab09423994596eb2c5ec2a971c17d1a3cb36dbf060592c730c725cd524b9067d7d2a1e031fef9ba7bd2ac6dc5ec9fb92aa693265f7be3987045254 1748 | languageName: node 1749 | linkType: hard 1750 | 1751 | "path-is-absolute@npm:^1.0.0": 1752 | version: 1.0.1 1753 | resolution: "path-is-absolute@npm:1.0.1" 1754 | checksum: 060840f92cf8effa293bcc1bea81281bd7d363731d214cbe5c227df207c34cd727430f70c6037b5159c8a870b9157cba65e775446b0ab06fd5ecc7e54615a3b8 1755 | languageName: node 1756 | linkType: hard 1757 | 1758 | "path-type@npm:^4.0.0": 1759 | version: 4.0.0 1760 | resolution: "path-type@npm:4.0.0" 1761 | checksum: 5b1e2daa247062061325b8fdbfd1fb56dde0a448fb1455453276ea18c60685bdad23a445dc148cf87bc216be1573357509b7d4060494a6fd768c7efad833ee45 1762 | languageName: node 1763 | linkType: hard 1764 | 1765 | "picomatch@npm:^2.0.4, picomatch@npm:^2.2.1, picomatch@npm:^2.3.1": 1766 | version: 2.3.1 1767 | resolution: "picomatch@npm:2.3.1" 1768 | checksum: 050c865ce81119c4822c45d3c84f1ced46f93a0126febae20737bd05ca20589c564d6e9226977df859ed5e03dc73f02584a2b0faad36e896936238238b0446cf 1769 | languageName: node 1770 | linkType: hard 1771 | 1772 | "pkg-conf@npm:^4.0.0": 1773 | version: 4.0.0 1774 | resolution: "pkg-conf@npm:4.0.0" 1775 | dependencies: 1776 | find-up: ^6.0.0 1777 | load-json-file: ^7.0.0 1778 | checksum: 6da0c064a74f6c7ae80d7d68c5853e14f7e762a2a80c6ca9e0aa827002b90b69c86fefe3bac830b10a6f1739e7f96a1f728637f2a141e50b0fdafe92a2c3eab6 1779 | languageName: node 1780 | linkType: hard 1781 | 1782 | "plur@npm:^5.1.0": 1783 | version: 5.1.0 1784 | resolution: "plur@npm:5.1.0" 1785 | dependencies: 1786 | irregular-plurals: ^3.3.0 1787 | checksum: 57e400dc4b926768fb0abab7f8688fe17e85673712134546e7beaaee188bae7e0504976e847d7e41d0d6103ff2fd61204095f03c2a45de19a8bad15aecb45cc1 1788 | languageName: node 1789 | linkType: hard 1790 | 1791 | "pretty-ms@npm:^7.0.1": 1792 | version: 7.0.1 1793 | resolution: "pretty-ms@npm:7.0.1" 1794 | dependencies: 1795 | parse-ms: ^2.1.0 1796 | checksum: d76c4920283b48be91f1d3797a2ce4bd51187d58d2a609ae993c028f73c92d16439449d857af57ccad91ae3a38b30c87307f5589749a056102ebb494c686957e 1797 | languageName: node 1798 | linkType: hard 1799 | 1800 | "promise-inflight@npm:^1.0.1": 1801 | version: 1.0.1 1802 | resolution: "promise-inflight@npm:1.0.1" 1803 | checksum: 22749483091d2c594261517f4f80e05226d4d5ecc1fc917e1886929da56e22b5718b7f2a75f3807e7a7d471bc3be2907fe92e6e8f373ddf5c64bae35b5af3981 1804 | languageName: node 1805 | linkType: hard 1806 | 1807 | "promise-retry@npm:^2.0.1": 1808 | version: 2.0.1 1809 | resolution: "promise-retry@npm:2.0.1" 1810 | dependencies: 1811 | err-code: ^2.0.2 1812 | retry: ^0.12.0 1813 | checksum: f96a3f6d90b92b568a26f71e966cbbc0f63ab85ea6ff6c81284dc869b41510e6cdef99b6b65f9030f0db422bf7c96652a3fff9f2e8fb4a0f069d8f4430359429 1814 | languageName: node 1815 | linkType: hard 1816 | 1817 | "queue-microtask@npm:^1.2.2": 1818 | version: 1.2.3 1819 | resolution: "queue-microtask@npm:1.2.3" 1820 | checksum: b676f8c040cdc5b12723ad2f91414d267605b26419d5c821ff03befa817ddd10e238d22b25d604920340fd73efd8ba795465a0377c4adf45a4a41e4234e42dc4 1821 | languageName: node 1822 | linkType: hard 1823 | 1824 | "readable-stream@npm:^3.6.0": 1825 | version: 3.6.0 1826 | resolution: "readable-stream@npm:3.6.0" 1827 | dependencies: 1828 | inherits: ^2.0.3 1829 | string_decoder: ^1.1.1 1830 | util-deprecate: ^1.0.1 1831 | checksum: d4ea81502d3799439bb955a3a5d1d808592cf3133350ed352aeaa499647858b27b1c4013984900238b0873ec8d0d8defce72469fb7a83e61d53f5ad61cb80dc8 1832 | languageName: node 1833 | linkType: hard 1834 | 1835 | "readdirp@npm:~3.6.0": 1836 | version: 3.6.0 1837 | resolution: "readdirp@npm:3.6.0" 1838 | dependencies: 1839 | picomatch: ^2.2.1 1840 | checksum: 1ced032e6e45670b6d7352d71d21ce7edf7b9b928494dcaba6f11fba63180d9da6cd7061ebc34175ffda6ff529f481818c962952004d273178acd70f7059b320 1841 | languageName: node 1842 | linkType: hard 1843 | 1844 | "require-directory@npm:^2.1.1": 1845 | version: 2.1.1 1846 | resolution: "require-directory@npm:2.1.1" 1847 | checksum: fb47e70bf0001fdeabdc0429d431863e9475e7e43ea5f94ad86503d918423c1543361cc5166d713eaa7029dd7a3d34775af04764bebff99ef413111a5af18c80 1848 | languageName: node 1849 | linkType: hard 1850 | 1851 | "resolve-cwd@npm:^3.0.0": 1852 | version: 3.0.0 1853 | resolution: "resolve-cwd@npm:3.0.0" 1854 | dependencies: 1855 | resolve-from: ^5.0.0 1856 | checksum: 546e0816012d65778e580ad62b29e975a642989108d9a3c5beabfb2304192fa3c9f9146fbdfe213563c6ff51975ae41bac1d3c6e047dd9572c94863a057b4d81 1857 | languageName: node 1858 | linkType: hard 1859 | 1860 | "resolve-from@npm:^5.0.0": 1861 | version: 5.0.0 1862 | resolution: "resolve-from@npm:5.0.0" 1863 | checksum: 4ceeb9113e1b1372d0cd969f3468fa042daa1dd9527b1b6bb88acb6ab55d8b9cd65dbf18819f9f9ddf0db804990901dcdaade80a215e7b2c23daae38e64f5bdf 1864 | languageName: node 1865 | linkType: hard 1866 | 1867 | "retry@npm:^0.12.0": 1868 | version: 0.12.0 1869 | resolution: "retry@npm:0.12.0" 1870 | checksum: 623bd7d2e5119467ba66202d733ec3c2e2e26568074923bc0585b6b99db14f357e79bdedb63cab56cec47491c4a0da7e6021a7465ca6dc4f481d3898fdd3158c 1871 | languageName: node 1872 | linkType: hard 1873 | 1874 | "reusify@npm:^1.0.4": 1875 | version: 1.0.4 1876 | resolution: "reusify@npm:1.0.4" 1877 | checksum: c3076ebcc22a6bc252cb0b9c77561795256c22b757f40c0d8110b1300723f15ec0fc8685e8d4ea6d7666f36c79ccc793b1939c748bf36f18f542744a4e379fcc 1878 | languageName: node 1879 | linkType: hard 1880 | 1881 | "rimraf@npm:^3.0.2": 1882 | version: 3.0.2 1883 | resolution: "rimraf@npm:3.0.2" 1884 | dependencies: 1885 | glob: ^7.1.3 1886 | bin: 1887 | rimraf: bin.js 1888 | checksum: 87f4164e396f0171b0a3386cc1877a817f572148ee13a7e113b238e48e8a9f2f31d009a92ec38a591ff1567d9662c6b67fd8818a2dbbaed74bc26a87a2a4a9a0 1889 | languageName: node 1890 | linkType: hard 1891 | 1892 | "run-parallel@npm:^1.1.9": 1893 | version: 1.2.0 1894 | resolution: "run-parallel@npm:1.2.0" 1895 | dependencies: 1896 | queue-microtask: ^1.2.2 1897 | checksum: cb4f97ad25a75ebc11a8ef4e33bb962f8af8516bb2001082ceabd8902e15b98f4b84b4f8a9b222e5d57fc3bd1379c483886ed4619367a7680dad65316993021d 1898 | languageName: node 1899 | linkType: hard 1900 | 1901 | "safe-buffer@npm:~5.2.0": 1902 | version: 5.2.1 1903 | resolution: "safe-buffer@npm:5.2.1" 1904 | checksum: b99c4b41fdd67a6aaf280fcd05e9ffb0813654894223afb78a31f14a19ad220bba8aba1cb14eddce1fcfb037155fe6de4e861784eb434f7d11ed58d1e70dd491 1905 | languageName: node 1906 | linkType: hard 1907 | 1908 | "safer-buffer@npm:>= 2.1.2 < 3.0.0": 1909 | version: 2.1.2 1910 | resolution: "safer-buffer@npm:2.1.2" 1911 | checksum: cab8f25ae6f1434abee8d80023d7e72b598cf1327164ddab31003c51215526801e40b66c5e65d658a0af1e9d6478cadcb4c745f4bd6751f97d8644786c0978b0 1912 | languageName: node 1913 | linkType: hard 1914 | 1915 | "semver@npm:^7.3.2, semver@npm:^7.3.5": 1916 | version: 7.3.7 1917 | resolution: "semver@npm:7.3.7" 1918 | dependencies: 1919 | lru-cache: ^6.0.0 1920 | bin: 1921 | semver: bin/semver.js 1922 | checksum: 2fa3e877568cd6ce769c75c211beaed1f9fce80b28338cadd9d0b6c40f2e2862bafd62c19a6cff42f3d54292b7c623277bcab8816a2b5521cf15210d43e75232 1923 | languageName: node 1924 | linkType: hard 1925 | 1926 | "serialize-error@npm:^7.0.1": 1927 | version: 7.0.1 1928 | resolution: "serialize-error@npm:7.0.1" 1929 | dependencies: 1930 | type-fest: ^0.13.1 1931 | checksum: e0aba4dca2fc9fe74ae1baf38dbd99190e1945445a241ba646290f2176cdb2032281a76443b02ccf0caf30da5657d510746506368889a593b9835a497fc0732e 1932 | languageName: node 1933 | linkType: hard 1934 | 1935 | "set-blocking@npm:^2.0.0": 1936 | version: 2.0.0 1937 | resolution: "set-blocking@npm:2.0.0" 1938 | checksum: 6e65a05f7cf7ebdf8b7c75b101e18c0b7e3dff4940d480efed8aad3a36a4005140b660fa1d804cb8bce911cac290441dc728084a30504d3516ac2ff7ad607b02 1939 | languageName: node 1940 | linkType: hard 1941 | 1942 | "signal-exit@npm:^3.0.7": 1943 | version: 3.0.7 1944 | resolution: "signal-exit@npm:3.0.7" 1945 | checksum: a2f098f247adc367dffc27845853e9959b9e88b01cb301658cfe4194352d8d2bb32e18467c786a7fe15f1d44b233ea35633d076d5e737870b7139949d1ab6318 1946 | languageName: node 1947 | linkType: hard 1948 | 1949 | "slash@npm:^3.0.0": 1950 | version: 3.0.0 1951 | resolution: "slash@npm:3.0.0" 1952 | checksum: 94a93fff615f25a999ad4b83c9d5e257a7280c90a32a7cb8b4a87996e4babf322e469c42b7f649fd5796edd8687652f3fb452a86dc97a816f01113183393f11c 1953 | languageName: node 1954 | linkType: hard 1955 | 1956 | "slash@npm:^4.0.0": 1957 | version: 4.0.0 1958 | resolution: "slash@npm:4.0.0" 1959 | checksum: da8e4af73712253acd21b7853b7e0dbba776b786e82b010a5bfc8b5051a1db38ed8aba8e1e8f400dd2c9f373be91eb1c42b66e91abb407ff42b10feece5e1d2d 1960 | languageName: node 1961 | linkType: hard 1962 | 1963 | "slice-ansi@npm:^5.0.0": 1964 | version: 5.0.0 1965 | resolution: "slice-ansi@npm:5.0.0" 1966 | dependencies: 1967 | ansi-styles: ^6.0.0 1968 | is-fullwidth-code-point: ^4.0.0 1969 | checksum: 7e600a2a55e333a21ef5214b987c8358fe28bfb03c2867ff2cbf919d62143d1812ac27b4297a077fdaf27a03da3678e49551c93e35f9498a3d90221908a1180e 1970 | languageName: node 1971 | linkType: hard 1972 | 1973 | "smart-buffer@npm:^4.2.0": 1974 | version: 4.2.0 1975 | resolution: "smart-buffer@npm:4.2.0" 1976 | checksum: b5167a7142c1da704c0e3af85c402002b597081dd9575031a90b4f229ca5678e9a36e8a374f1814c8156a725d17008ae3bde63b92f9cfd132526379e580bec8b 1977 | languageName: node 1978 | linkType: hard 1979 | 1980 | "socks-proxy-agent@npm:^6.1.1": 1981 | version: 6.2.0 1982 | resolution: "socks-proxy-agent@npm:6.2.0" 1983 | dependencies: 1984 | agent-base: ^6.0.2 1985 | debug: ^4.3.3 1986 | socks: ^2.6.2 1987 | checksum: 6723fd64fb50334e2b340fd0a80fd8488ffc5bc43d85b7cf1d25612044f814dd7d6ea417fd47602159941236f7f4bd15669fa5d7e1f852598a31288e1a43967b 1988 | languageName: node 1989 | linkType: hard 1990 | 1991 | "socks@npm:^2.6.2": 1992 | version: 2.6.2 1993 | resolution: "socks@npm:2.6.2" 1994 | dependencies: 1995 | ip: ^1.1.5 1996 | smart-buffer: ^4.2.0 1997 | checksum: dd9194293059d737759d5c69273850ad4149f448426249325c4bea0e340d1cf3d266c3b022694b0dcf5d31f759de23657244c481fc1e8322add80b7985c36b5e 1998 | languageName: node 1999 | linkType: hard 2000 | 2001 | "sprintf-js@npm:~1.0.2": 2002 | version: 1.0.3 2003 | resolution: "sprintf-js@npm:1.0.3" 2004 | checksum: 19d79aec211f09b99ec3099b5b2ae2f6e9cdefe50bc91ac4c69144b6d3928a640bb6ae5b3def70c2e85a2c3d9f5ec2719921e3a59d3ca3ef4b2fd1a4656a0df3 2005 | languageName: node 2006 | linkType: hard 2007 | 2008 | "ssri@npm:^9.0.0": 2009 | version: 9.0.0 2010 | resolution: "ssri@npm:9.0.0" 2011 | dependencies: 2012 | minipass: ^3.1.1 2013 | checksum: bf33174232d07cc64e77ab1c51b55d28352273380c503d35642a19627e88a2c5f160039bb0a28608a353485075dda084dbf0390c7070f9f284559eb71d01b84b 2014 | languageName: node 2015 | linkType: hard 2016 | 2017 | "stack-utils@npm:^2.0.5": 2018 | version: 2.0.5 2019 | resolution: "stack-utils@npm:2.0.5" 2020 | dependencies: 2021 | escape-string-regexp: ^2.0.0 2022 | checksum: 76b69da0f5b48a34a0f93c98ee2a96544d2c4ca2557f7eef5ddb961d3bdc33870b46f498a84a7c4f4ffb781df639840e7ebf6639164ed4da5e1aeb659615b9c7 2023 | languageName: node 2024 | linkType: hard 2025 | 2026 | "string-width@npm:^1.0.2 || 2 || 3 || 4, string-width@npm:^4.1.0, string-width@npm:^4.2.0, string-width@npm:^4.2.3": 2027 | version: 4.2.3 2028 | resolution: "string-width@npm:4.2.3" 2029 | dependencies: 2030 | emoji-regex: ^8.0.0 2031 | is-fullwidth-code-point: ^3.0.0 2032 | strip-ansi: ^6.0.1 2033 | checksum: e52c10dc3fbfcd6c3a15f159f54a90024241d0f149cf8aed2982a2d801d2e64df0bf1dc351cf8e95c3319323f9f220c16e740b06faecd53e2462df1d2b5443fb 2034 | languageName: node 2035 | linkType: hard 2036 | 2037 | "string-width@npm:^5.0.0": 2038 | version: 5.1.2 2039 | resolution: "string-width@npm:5.1.2" 2040 | dependencies: 2041 | eastasianwidth: ^0.2.0 2042 | emoji-regex: ^9.2.2 2043 | strip-ansi: ^7.0.1 2044 | checksum: 7369deaa29f21dda9a438686154b62c2c5f661f8dda60449088f9f980196f7908fc39fdd1803e3e01541970287cf5deae336798337e9319a7055af89dafa7193 2045 | languageName: node 2046 | linkType: hard 2047 | 2048 | "string_decoder@npm:^1.1.1": 2049 | version: 1.3.0 2050 | resolution: "string_decoder@npm:1.3.0" 2051 | dependencies: 2052 | safe-buffer: ~5.2.0 2053 | checksum: 8417646695a66e73aefc4420eb3b84cc9ffd89572861fe004e6aeb13c7bc00e2f616247505d2dbbef24247c372f70268f594af7126f43548565c68c117bdeb56 2054 | languageName: node 2055 | linkType: hard 2056 | 2057 | "strip-ansi@npm:^6.0.0, strip-ansi@npm:^6.0.1": 2058 | version: 6.0.1 2059 | resolution: "strip-ansi@npm:6.0.1" 2060 | dependencies: 2061 | ansi-regex: ^5.0.1 2062 | checksum: f3cd25890aef3ba6e1a74e20896c21a46f482e93df4a06567cebf2b57edabb15133f1f94e57434e0a958d61186087b1008e89c94875d019910a213181a14fc8c 2063 | languageName: node 2064 | linkType: hard 2065 | 2066 | "strip-ansi@npm:^7.0.1": 2067 | version: 7.0.1 2068 | resolution: "strip-ansi@npm:7.0.1" 2069 | dependencies: 2070 | ansi-regex: ^6.0.1 2071 | checksum: 257f78fa433520e7f9897722731d78599cb3fce29ff26a20a5e12ba4957463b50a01136f37c43707f4951817a75e90820174853d6ccc240997adc5df8f966039 2072 | languageName: node 2073 | linkType: hard 2074 | 2075 | "supertap@npm:^3.0.1": 2076 | version: 3.0.1 2077 | resolution: "supertap@npm:3.0.1" 2078 | dependencies: 2079 | indent-string: ^5.0.0 2080 | js-yaml: ^3.14.1 2081 | serialize-error: ^7.0.1 2082 | strip-ansi: ^7.0.1 2083 | checksum: ee3d71c1d25f7f15d4a849e72b0c5f430df7cd8f702cf082fdbec5642a9546be6557766745655fa3a3e9c88f7c7eed849f2d74457b5b72cb9d94a779c0c8a948 2084 | languageName: node 2085 | linkType: hard 2086 | 2087 | "tar@npm:^6.1.11, tar@npm:^6.1.2": 2088 | version: 6.1.11 2089 | resolution: "tar@npm:6.1.11" 2090 | dependencies: 2091 | chownr: ^2.0.0 2092 | fs-minipass: ^2.0.0 2093 | minipass: ^3.0.0 2094 | minizlib: ^2.1.1 2095 | mkdirp: ^1.0.3 2096 | yallist: ^4.0.0 2097 | checksum: a04c07bb9e2d8f46776517d4618f2406fb977a74d914ad98b264fc3db0fe8224da5bec11e5f8902c5b9bcb8ace22d95fbe3c7b36b8593b7dfc8391a25898f32f 2098 | languageName: node 2099 | linkType: hard 2100 | 2101 | "temp-dir@npm:^2.0.0": 2102 | version: 2.0.0 2103 | resolution: "temp-dir@npm:2.0.0" 2104 | checksum: cc4f0404bf8d6ae1a166e0e64f3f409b423f4d1274d8c02814a59a5529f07db6cd070a749664141b992b2c1af337fa9bb451a460a43bb9bcddc49f235d3115aa 2105 | languageName: node 2106 | linkType: hard 2107 | 2108 | "time-zone@npm:^1.0.0": 2109 | version: 1.0.0 2110 | resolution: "time-zone@npm:1.0.0" 2111 | checksum: e46f5a69b8c236dcd8e91e29d40d4e7a3495ed4f59888c3f84ce1d9678e20461421a6ba41233509d47dd94bc18f1a4377764838b21b584663f942b3426dcbce8 2112 | languageName: node 2113 | linkType: hard 2114 | 2115 | "to-regex-range@npm:^5.0.1": 2116 | version: 5.0.1 2117 | resolution: "to-regex-range@npm:5.0.1" 2118 | dependencies: 2119 | is-number: ^7.0.0 2120 | checksum: f76fa01b3d5be85db6a2a143e24df9f60dd047d151062d0ba3df62953f2f697b16fe5dad9b0ac6191c7efc7b1d9dcaa4b768174b7b29da89d4428e64bc0a20ed 2121 | languageName: node 2122 | linkType: hard 2123 | 2124 | "txforge@workspace:.": 2125 | version: 0.0.0-use.local 2126 | resolution: "txforge@workspace:." 2127 | dependencies: 2128 | "@runonbitcoin/nimble": ^1.0.13 2129 | ava: ^4.2.0 2130 | esbuild: ^0.14.38 2131 | esbuild-plugin-globals: ^0.1.1 2132 | languageName: unknown 2133 | linkType: soft 2134 | 2135 | "type-fest@npm:^0.13.1": 2136 | version: 0.13.1 2137 | resolution: "type-fest@npm:0.13.1" 2138 | checksum: e6bf2e3c449f27d4ef5d56faf8b86feafbc3aec3025fc9a5fbe2db0a2587c44714521f9c30d8516a833c8c506d6263f5cc11267522b10c6ccdb6cc55b0a9d1c4 2139 | languageName: node 2140 | linkType: hard 2141 | 2142 | "unique-filename@npm:^1.1.1": 2143 | version: 1.1.1 2144 | resolution: "unique-filename@npm:1.1.1" 2145 | dependencies: 2146 | unique-slug: ^2.0.0 2147 | checksum: cf4998c9228cc7647ba7814e255dec51be43673903897b1786eff2ac2d670f54d4d733357eb08dea969aa5e6875d0e1bd391d668fbdb5a179744e7c7551a6f80 2148 | languageName: node 2149 | linkType: hard 2150 | 2151 | "unique-slug@npm:^2.0.0": 2152 | version: 2.0.2 2153 | resolution: "unique-slug@npm:2.0.2" 2154 | dependencies: 2155 | imurmurhash: ^0.1.4 2156 | checksum: 5b6876a645da08d505dedb970d1571f6cebdf87044cb6b740c8dbb24f0d6e1dc8bdbf46825fd09f994d7cf50760e6f6e063cfa197d51c5902c00a861702eb75a 2157 | languageName: node 2158 | linkType: hard 2159 | 2160 | "util-deprecate@npm:^1.0.1": 2161 | version: 1.0.2 2162 | resolution: "util-deprecate@npm:1.0.2" 2163 | checksum: 474acf1146cb2701fe3b074892217553dfcf9a031280919ba1b8d651a068c9b15d863b7303cb15bd00a862b498e6cf4ad7b4a08fb134edd5a6f7641681cb54a2 2164 | languageName: node 2165 | linkType: hard 2166 | 2167 | "well-known-symbols@npm:^2.0.0": 2168 | version: 2.0.0 2169 | resolution: "well-known-symbols@npm:2.0.0" 2170 | checksum: 4f54bbc3012371cb4d228f436891b8e7536d34ac61a57541890257e96788608e096231e0121ac24d08ef2f908b3eb2dc0adba35023eaeb2a7df655da91415402 2171 | languageName: node 2172 | linkType: hard 2173 | 2174 | "which@npm:^2.0.2": 2175 | version: 2.0.2 2176 | resolution: "which@npm:2.0.2" 2177 | dependencies: 2178 | isexe: ^2.0.0 2179 | bin: 2180 | node-which: ./bin/node-which 2181 | checksum: 1a5c563d3c1b52d5f893c8b61afe11abc3bab4afac492e8da5bde69d550de701cf9806235f20a47b5c8fa8a1d6a9135841de2596535e998027a54589000e66d1 2182 | languageName: node 2183 | linkType: hard 2184 | 2185 | "wide-align@npm:^1.1.5": 2186 | version: 1.1.5 2187 | resolution: "wide-align@npm:1.1.5" 2188 | dependencies: 2189 | string-width: ^1.0.2 || 2 || 3 || 4 2190 | checksum: d5fc37cd561f9daee3c80e03b92ed3e84d80dde3365a8767263d03dacfc8fa06b065ffe1df00d8c2a09f731482fcacae745abfbb478d4af36d0a891fad4834d3 2191 | languageName: node 2192 | linkType: hard 2193 | 2194 | "wrap-ansi@npm:^7.0.0": 2195 | version: 7.0.0 2196 | resolution: "wrap-ansi@npm:7.0.0" 2197 | dependencies: 2198 | ansi-styles: ^4.0.0 2199 | string-width: ^4.1.0 2200 | strip-ansi: ^6.0.0 2201 | checksum: a790b846fd4505de962ba728a21aaeda189b8ee1c7568ca5e817d85930e06ef8d1689d49dbf0e881e8ef84436af3a88bc49115c2e2788d841ff1b8b5b51a608b 2202 | languageName: node 2203 | linkType: hard 2204 | 2205 | "wrappy@npm:1": 2206 | version: 1.0.2 2207 | resolution: "wrappy@npm:1.0.2" 2208 | checksum: 159da4805f7e84a3d003d8841557196034155008f817172d4e986bd591f74aa82aa7db55929a54222309e01079a65a92a9e6414da5a6aa4b01ee44a511ac3ee5 2209 | languageName: node 2210 | linkType: hard 2211 | 2212 | "write-file-atomic@npm:^4.0.1": 2213 | version: 4.0.1 2214 | resolution: "write-file-atomic@npm:4.0.1" 2215 | dependencies: 2216 | imurmurhash: ^0.1.4 2217 | signal-exit: ^3.0.7 2218 | checksum: 8f780232533ca6223c63c9b9c01c4386ca8c625ebe5017a9ed17d037aec19462ae17109e0aa155bff5966ee4ae7a27b67a99f55caf3f32ffd84155e9da3929fc 2219 | languageName: node 2220 | linkType: hard 2221 | 2222 | "y18n@npm:^5.0.5": 2223 | version: 5.0.8 2224 | resolution: "y18n@npm:5.0.8" 2225 | checksum: 54f0fb95621ee60898a38c572c515659e51cc9d9f787fb109cef6fde4befbe1c4602dc999d30110feee37456ad0f1660fa2edcfde6a9a740f86a290999550d30 2226 | languageName: node 2227 | linkType: hard 2228 | 2229 | "yallist@npm:^4.0.0": 2230 | version: 4.0.0 2231 | resolution: "yallist@npm:4.0.0" 2232 | checksum: 343617202af32df2a15a3be36a5a8c0c8545208f3d3dfbc6bb7c3e3b7e8c6f8e7485432e4f3b88da3031a6e20afa7c711eded32ddfb122896ac5d914e75848d5 2233 | languageName: node 2234 | linkType: hard 2235 | 2236 | "yargs-parser@npm:^21.0.0": 2237 | version: 21.0.1 2238 | resolution: "yargs-parser@npm:21.0.1" 2239 | checksum: c3ea2ed12cad0377ce3096b3f138df8267edf7b1aa7d710cd502fe16af417bafe4443dd71b28158c22fcd1be5dfd0e86319597e47badf42ff83815485887323a 2240 | languageName: node 2241 | linkType: hard 2242 | 2243 | "yargs@npm:^17.3.1": 2244 | version: 17.4.1 2245 | resolution: "yargs@npm:17.4.1" 2246 | dependencies: 2247 | cliui: ^7.0.2 2248 | escalade: ^3.1.1 2249 | get-caller-file: ^2.0.5 2250 | require-directory: ^2.1.1 2251 | string-width: ^4.2.3 2252 | y18n: ^5.0.5 2253 | yargs-parser: ^21.0.0 2254 | checksum: e9012322870d7e4e912a6ae1f63b203e365f911c0cf158be92c36edefddfb3bd38ce17eb9ef0d18858a4777f047c50589ea22dacb44bd949169ba37dc6d34bee 2255 | languageName: node 2256 | linkType: hard 2257 | 2258 | "yocto-queue@npm:^1.0.0": 2259 | version: 1.0.0 2260 | resolution: "yocto-queue@npm:1.0.0" 2261 | checksum: 2cac84540f65c64ccc1683c267edce396b26b1e931aa429660aefac8fbe0188167b7aee815a3c22fa59a28a58d898d1a2b1825048f834d8d629f4c2a5d443801 2262 | languageName: node 2263 | linkType: hard 2264 | --------------------------------------------------------------------------------