diff --git a/.github/workflows/main_ci.yml b/.github/workflows/main_ci.yml
index a9fdc58be..cbe825744 100644
--- a/.github/workflows/main_ci.yml
+++ b/.github/workflows/main_ci.yml
@@ -6,89 +6,142 @@ on:
- master
pull_request:
+permissions:
+ contents: read
+ pages: write
+ id-token: write
+
+
jobs:
- audit:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v2
- - uses: actions/setup-node@v1
- with:
- node-version: 12
- registry-url: https://registry.npmjs.org/
- - run: npm ci
- - run: npm run audit
+ ##################
+ # Jobs with matrix
+ ##################
unit:
runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ node-version: [18, 'lts/*']
+ # See supported Node.js release schedule at https://nodejs.org/en/about/releases/
steps:
- - uses: actions/checkout@v2
- - uses: actions/setup-node@v1
+ - uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 # v2
+ - name: Use Node.js ${{ matrix.node-version }}
+ uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6
with:
- node-version: 12
+ node-version: ${{ matrix.node-version }}
registry-url: https://registry.npmjs.org/
+ cache: 'npm'
- run: npm ci
- run: npm run unit
- coverage:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v2
- - uses: actions/setup-node@v1
- with:
- node-version: 12
- registry-url: https://registry.npmjs.org/
- - run: npm ci
- - run: npm run coverage
integration:
runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ node-version: [18, 'lts/*']
+ # See supported Node.js release schedule at https://nodejs.org/en/about/releases/
services:
regtest:
- image: junderw/bitcoinjs-regtest-server@sha256:a46ec1a651ca5b1a5408f2b2526ea5f435421dd2bc2f28fae3bc33e1fd614552
+ image: junderw/bitcoinjs-regtest-server@sha256:5b69cf95d9edf6d5b3a00504665d6b3c382a6aa3728fe8ce897974c519061463
ports:
- 8080:8080
steps:
- - uses: actions/checkout@v2
- - uses: actions/setup-node@v1
+ - uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 # v2
+ - name: Use Node.js ${{ matrix.node-version }}
+ uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6
with:
- node-version: 12
+ node-version: ${{ matrix.node-version }}
registry-url: https://registry.npmjs.org/
+ cache: 'npm'
- run: npm ci
- run: APIURL=http://127.0.0.1:8080/1 npm run integration
+
+
+
+ #####################
+ # Jobs without matrix
+ #####################
+ coverage:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 # v2
+ - uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6
+ with:
+ node-version: 'lts/*'
+ registry-url: https://registry.npmjs.org/
+ cache: 'npm'
+ - run: npm ci
+ - run: npm run coverage
format:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v2
- - uses: actions/setup-node@v1
+ - uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 # v2
+ - uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6
with:
- node-version: 12
+ node-version: 'lts/*'
registry-url: https://registry.npmjs.org/
+ cache: 'npm'
- run: npm ci
- run: npm run format:ci
gitdiff:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v2
- - uses: actions/setup-node@v1
+ - uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 # v2
+ - uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6
with:
- node-version: 12
+ node-version: 'lts/*'
registry-url: https://registry.npmjs.org/
+ cache: 'npm'
- run: npm ci
- run: npm run gitdiff:ci
lint:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v2
- - uses: actions/setup-node@v1
+ - uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 # v2
+ - uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6
with:
- node-version: 12
+ node-version: 'lts/*'
registry-url: https://registry.npmjs.org/
+ cache: 'npm'
- run: npm ci
- run: npm run lint
lint-tests:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v2
- - uses: actions/setup-node@v1
+ - uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 # v2
+ - uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6
with:
- node-version: 12
+ node-version: 'lts/*'
registry-url: https://registry.npmjs.org/
+ cache: 'npm'
- run: npm ci
- run: npm run lint:tests
+
+ build-doc:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 # v2
+ - uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6
+ with:
+ node-version: 'lts/*'
+ registry-url: https://registry.npmjs.org/
+ cache: 'npm'
+ - run: npm ci
+ - run: npm run doc
+ - name: Upload artifact
+ uses: actions/upload-pages-artifact@v3
+ with:
+ name: github-pages
+ path: ./docs
+
+ deploy-doc:
+ if: github.ref == 'refs/heads/master'
+ environment:
+ name: github-pages
+ url: ${{ steps.deployment.outputs.page_url }}
+ runs-on: ubuntu-latest
+ needs: build-doc
+ steps:
+ - name: Deploy to GitHub Pages
+ id: deployment
+ uses: actions/deploy-pages@v4
diff --git a/.github/workflows/package-publish.yml b/.github/workflows/package-publish.yml
new file mode 100644
index 000000000..1d0c6561f
--- /dev/null
+++ b/.github/workflows/package-publish.yml
@@ -0,0 +1,21 @@
+name: package-publish
+
+on: workflow_dispatch
+
+jobs:
+ package-publish:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+ - uses: actions/setup-node@v2
+ with:
+ node-version: '20.x'
+ registry-url: 'https://registry.npmjs.org'
+ - name: Install Package
+ run: npm ci
+ - name: Build
+ run: npm run build
+ - name: Publish
+ env:
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
+ run: npm whoami && npm publish --access public
diff --git a/.gitignore b/.gitignore
index d6ceb6b58..664a5520f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,3 +5,5 @@ npm-debug.log
test/*.js
test/integration/*.js
!test/ts-node-register.js
+docs
+plugin
\ No newline at end of file
diff --git a/.mocharc.json b/.mocharc.json
new file mode 100644
index 000000000..3cfb40bae
--- /dev/null
+++ b/.mocharc.json
@@ -0,0 +1,4 @@
+{
+ "$schema": "https://json.schemastore.org/mocharc.json",
+ "require": "tsx"
+}
\ No newline at end of file
diff --git a/.npm-audit-whitelister.json b/.npm-audit-whitelister.json
deleted file mode 100644
index fe51488c7..000000000
--- a/.npm-audit-whitelister.json
+++ /dev/null
@@ -1 +0,0 @@
-[]
diff --git a/.nsprc b/.nsprc
new file mode 100644
index 000000000..0967ef424
--- /dev/null
+++ b/.nsprc
@@ -0,0 +1 @@
+{}
diff --git a/.prettierignore b/.prettierignore
index e69de29bb..191ae4cc9 100644
--- a/.prettierignore
+++ b/.prettierignore
@@ -0,0 +1 @@
+*.d.ts
\ No newline at end of file
diff --git a/.prettierrc.json b/.prettierrc.json
index a20502b7f..ccb9a0c17 100644
--- a/.prettierrc.json
+++ b/.prettierrc.json
@@ -1,4 +1,7 @@
{
"singleQuote": true,
- "trailingComma": "all"
+ "trailingComma": "all",
+ "endOfLine": "auto",
+ "arrowParens": "avoid",
+ "tabWidth": 2
}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f7d8f914f..9dd716cae 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,57 @@
+# 6.1.7
+__added__
+- skip ecc library verification via DANGER_DO_NOT_VERIFY_ECCLIB flag
+
+# 6.1.6
+__fixed__
+- Fix sighash treatment when signing taproot script sign scripts using Psbt (#2104)
+- Fix error for invalid scripts in toASM (#2097)
+- Fix mutation of input to addOutput method on Psbt (#2091)
+
+# 6.1.5
+__fixed__
+- Updated bip174 dependency to fix issue with unknownKeyVals. (#1979)
+
+# 6.1.4
+__changed__
+- Changed internal usage of the Buffer API to match with newer broken bundlers that don't follow spec. The new usage is still compatible with older versions of Buffer, so there shouldn't be any breakage. The public API interface was not changed. (#1975)
+
+# 6.1.3
+__fixed__
+- validateSignaturesOfInput for taproot inputs returned false for valid signatures in specific cases. (#1934)
+
+# 6.1.2
+__fixed__
+- validateSignaturesOfInput for taproot inputs returned true for invalid signatures in specific cases. (#1932)
+
+# 6.1.1
+__added__
+- add example using BIP86 vector to verify the sending to and from a BIP86 generated taproot address
+
+__fixed__
+- support for 65 byte taproot signature
+- prevent the creation of unspendable scripts in bitcoinjs-lib by implementing checks for resource limitations
+- use `Buffer.from()` instead of `Buffer.of()`
+
+__changed__
+- performance: precompute the taproot hashes
+- performance: switch from `create-hash` and `ripemd160` to noble-hashes
+
+__removed__
+- types: removed unused methods `privateAdd` and `privateNegate` from `TinySecp256k1Interface`
+
+# 6.1.0
+__added__
+- taproot support for payments (p2tr) and PSBT. See taproot.spec.ts integration test for examples. (#1742)
+
+# 6.0.2
+__fixed__
+- p2sh payment now uses empty Buffer for redeem.output when redeemScript is OP_FALSE (#1802)
+- Fix ripemd160 hashing fallback issue (#1812)
+
+# 6.0.1
+- No changes to public API
+
# 6.0.0
__removed__
- bip32: Removed the re-export. Please add as dependency to your app instead.
@@ -283,18 +337,18 @@ __fixed__
# 2.1.0
From this release users should use the HDNode directly (compared to accessing `.keyPair`) when performing ECDSA operations such as `sign` or `verify`.
-Ideally you shoud not have to directly access `HDNode` internals for general usage, as it can often be confusing and error prone.
+Ideally you should not have to directly access `HDNode` internals for general usage, as it can often be confusing and error prone.
__added__
- `ECPair.prototype.getNetwork`
-- `HDNode.prototype.getNetwork`, wraps the underyling keyPair's `getNetwork` method
-- `HDNode.prototype.getPublicKeyBuffer`, wraps the underyling keyPair's `getPublicKeyBuffer` method
+- `HDNode.prototype.getNetwork`, wraps the underlying keyPair's `getNetwork` method
+- `HDNode.prototype.getPublicKeyBuffer`, wraps the underlying keyPair's `getPublicKeyBuffer` method
- `HDNode.prototype.sign`, wraps the underlying keyPair's `sign` method
- `HDNode.prototype.verify`, wraps the underlying keyPair's `verify` method
# 2.0.0
-In this release we have strived to simplify the API, [using native types](https://github.com/bitcoinjs/bitcoinjs-lib/issues/407) wherevever possible to encourage cross-compatibility with other open source community modules.
+In this release we have strived to simplify the API, [using native types](https://github.com/bitcoinjs/bitcoinjs-lib/issues/407) wherever possible to encourage cross-compatibility with other open source community modules.
The `ecdsa` module has been removed in lieu of using a new ECDSA module (for performance and safety reasons) during the `2.x.y` major release.
Several other cumbersome modules have been removed, with their new independent modules recommended for usage instead for greater modularity in your projects.
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 2c72633de..7797c92d2 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -14,8 +14,8 @@ We are always accepting of pull requests, but we do adhere to specific standards
GitHub is the preferred method of communication between members.
Otherwise, in order of preference:
-* bitcoinjs.slack.com
-* #bitcoinjs-dev on Freenode IRC
+* #bitcoinjs-dev:matrix.org on Matrix (A part of the #bitcoinjs-space:matrix.org "Space")
+* #bitcoinjs on libera.chat IRC
## Workflow
@@ -50,7 +50,7 @@ Refer to the [Git manual](https://git-scm.com/doc) for any information about `gi
## Regarding TypeScript
-This library is written in TypeScript with tslint, prettier, and the tsc transpiler. These tools will help during testing to notice improper logic before committing and sending a pull request.
+This library is written in TypeScript with eslint, prettier, and the tsc transpiler. These tools will help during testing to notice improper logic before committing and sending a pull request.
Some rules regarding TypeScript:
diff --git a/LICENSE b/LICENSE
index 0383ebc04..3b7466daf 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
The MIT License (MIT)
-Copyright (c) 2011-2020 bitcoinjs-lib contributors
+Copyright (c) 2011-2025 bitcoinjs-lib contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/README.md b/README.md
index 9389a73e0..857fef70e 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,5 @@
-# BitcoinJS (bitcoinjs-lib)
+# bitcoinjs-lib
+
[](https://github.com/bitcoinjs/bitcoinjs-lib/actions/workflows/main_ci.yml) [](https://www.npmjs.org/package/bitcoinjs-lib) [](https://github.com/prettier/prettier)
A javascript Bitcoin library for node.js and browsers. Written in TypeScript, but committing the JS files to verify.
@@ -9,7 +10,6 @@ Released under the terms of the [MIT LICENSE](LICENSE).
If you are thinking of using the *master* branch of this library in production, **stop**.
Master is not stable; it is our development branch, and [only tagged releases may be classified as stable](https://github.com/bitcoinjs/bitcoinjs-lib/tags).
-
## Can I trust this code?
> Don't trust. Verify.
@@ -23,12 +23,24 @@ Mistakes and bugs happen, but with your help in resolving and reporting [issues]
- Standardized, using [prettier](https://github.com/prettier/prettier) and Node `Buffer`'s throughout, and
- Friendly, with a strong and helpful community, ready to answer questions.
-
## Documentation
-Presently, we do not have any formal documentation other than our [examples](#examples), please [ask for help](https://github.com/bitcoinjs/bitcoinjs-lib/issues/new) if our examples aren't enough to guide you.
+Visit our [documentation](https://bitcoinjs.github.io/bitcoinjs-lib/) to explore the available resources. We're continually enhancing our documentation with additional features for an enriched experience. If you need further guidance beyond what our [examples](#examples) offer, don't hesitate to [ask for help](https://github.com/bitcoinjs/bitcoinjs-lib/issues/new). We're here to assist you.
You can find a [Web UI](https://bitcoincore.tech/apps/bitcoinjs-ui/index.html) that covers most of the `psbt.ts`, `transaction.ts` and `p2*.ts` APIs [here](https://bitcoincore.tech/apps/bitcoinjs-ui/index.html).
+## How can I contact the developers outside of Github?
+**Most of the time, this is not appropriate. Creating issues and pull requests in the open will help others with similar issues, so please try to use public issues and pull requests for communication.**
+
+That said, sometimes developers might be open to taking things off the record (ie. You want to share code that you don't want public to get help with it). In that case, please negotiate on the public issues as to where you will contact.
+
+We have created public rooms on IRC (`#bitcoinjs` on `libera.chat`) and Matrix (`#bitcoinjs-dev:matrix.org`). These two channels have been joined together in a Matrix "Space" which has the Matrix room AND an IRC bridge room that can converse with the IRC room. The "Space" is `#bitcoinjs-space:matrix.org`.
+
+Matrix and IRC both have functions for direct messaging, but IRC is not end to end encrypted, so Matrix is recommended for most communication. The official Matrix client maintained by the Matrix core team is called "Element" and can be downloaded here: https://element.io/download (Account creation is free on the matrix.org server, which is the default setting for Element.)
+
+We used to have a Slack. It is dead. If you find it, no one will answer you most likely.
+
+No we will not make a Discord.
+
## Installation
``` bash
npm install bitcoinjs-lib
@@ -61,13 +73,13 @@ Unfortunately, this isn't a silver bullet.
Often, Javascript itself is working against us by bypassing these counter-measures.
Problems in [`Buffer (UInt8Array)`](https://github.com/feross/buffer), for example, can trivially result in **catastrophic fund loss** without any warning.
-It can do this through undermining your random number generation, accidentally producing a [duplicate `k` value](https://www.nilsschneider.net/2013/01/28/recovering-bitcoin-private-keys.html), sending Bitcoin to a malformed output script, or any of a million different ways.
+It can do this through undermining your random number generation, accidentally producing a [duplicate `k` value](https://web.archive.org/web/20160308014317/http://www.nilsschneider.net/2013/01/28/recovering-bitcoin-private-keys.html), sending Bitcoin to a malformed output script, or any of a million different ways.
Running tests in your target environment is important and a recommended step to verify continuously.
Finally, **adhere to best practice**.
-We are not an authorative source of best practice, but, at the very least:
+We are not an authoritative source of best practice, but, at the very least:
-* [Don't re-use addresses](https://en.bitcoin.it/wiki/Address_reuse).
+* [Don't reuse addresses](https://en.bitcoin.it/wiki/Address_reuse).
* Don't share BIP32 extended public keys ('xpubs'). [They are a liability](https://bitcoin.stackexchange.com/questions/56916/derivation-of-parent-private-key-from-non-hardened-child), and it only takes 1 misplaced private key (or a buggy implementation!) and you are vulnerable to **catastrophic fund loss**.
* [Don't use `Math.random`](https://security.stackexchange.com/questions/181580/why-is-math-random-not-designed-to-be-cryptographically-secure) - in any way - don't.
* Enforce that users always verify (manually) a freshly-decoded human-readable version of their intended transaction before broadcast.
@@ -76,8 +88,30 @@ We are not an authorative source of best practice, but, at the very least:
### Browser
-The recommended method of using `bitcoinjs-lib` in your browser is through [Browserify](https://github.com/substack/node-browserify).
-If you're familiar with how to use browserify, ignore this and carry on, otherwise, it is recommended to read the tutorial at https://browserify.org/.
+The recommended method of using `bitcoinjs-lib` in your browser is through [browserify](http://browserify.org/).
+
+If you'd like to use a different (more modern) build tool than `browserify`, you can compile just this library and its dependencies into a single JavaScript file:
+
+```sh
+$ npm install bitcoinjs-lib browserify
+$ npx browserify --standalone bitcoin -o bitcoinjs-lib.js <<< "module.exports = require('bitcoinjs-lib');"
+```
+
+Which you can then import as an ESM module:
+
+```javascript
+
+````
+
+#### Using Taproot:
+When utilizing Taproot features with bitcoinjs-lib, you may need to include an additional ECC (Elliptic Curve Cryptography) library. The commonly used `tiny-secp256k1` library, however, might lead to compatibility issues due to its reliance on WASM (WebAssembly). The following alternatives may be used instead, though they may be significantly slower for high volume of signing and pubkey deriving operations.
+
+#### Alternatives for ECC Library:
+1. `@bitcoin-js/tiny-secp256k1-asmjs`
+ A version of `tiny-secp256k1` compiled to ASM.js directly from the WASM version, potentially better supported in browsers. This is the slowest option.
+2. `@bitcoinerlab/secp256k1`
+ Another alternative library for ECC functionality. This requires access to the global `BigInt` primitive.
+For advantages and detailed comparison of these libraries, visit: [tiny-secp256k1 GitHub page](https://github.com/bitcoinjs/tiny-secp256k1).
**NOTE**: We use Node Maintenance LTS features, if you need strict ES5, use [`--transform babelify`](https://github.com/babel/babelify) in conjunction with your `browserify` step (using an [`es2015`](https://babeljs.io/docs/plugins/preset-es2015/) preset).
@@ -91,8 +125,11 @@ The below examples are implemented as integration tests, they should be very eas
Otherwise, pull requests are appreciated.
Some examples interact (via HTTPS) with a 3rd Party Blockchain Provider (3PBP).
-- [Taproot Key Spend](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/taproot.md)
+- [Taproot Key Spend](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/taproot.spec.ts)
+- [Create (and broadcast via 3PBP) a taproot script-path spend Transaction - OP_CHECKSIG](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/taproot.spec.ts)
+- [Create (and broadcast via 3PBP) a taproot script-path spend Transaction - OP_CHECKSEQUENCEVERIFY](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/taproot.spec.ts)
+- [Create (and broadcast via 3PBP) a taproot script-path spend Transaction - OP_CHECKSIGADD (3-of-3)](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/taproot.spec.ts)
- [Generate a random address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.spec.ts)
- [Import an address via WIF](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.spec.ts)
- [Generate a 2-of-3 P2SH multisig address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.spec.ts)
diff --git a/eslint.config.js b/eslint.config.js
new file mode 100644
index 000000000..a361d046e
--- /dev/null
+++ b/eslint.config.js
@@ -0,0 +1,78 @@
+import typescriptEslint from "@typescript-eslint/eslint-plugin";
+import globals from "globals";
+import tsParser from "@typescript-eslint/parser";
+import path from "node:path";
+import { fileURLToPath } from "node:url";
+import js from "@eslint/js";
+import { FlatCompat } from "@eslint/eslintrc";
+
+const __filename = fileURLToPath(import.meta.url);
+const __dirname = path.dirname(__filename);
+const compat = new FlatCompat({
+ baseDirectory: __dirname,
+ recommendedConfig: js.configs.recommended,
+ allConfig: js.configs.all
+});
+
+export default [...compat.extends(
+ "eslint:recommended",
+ "prettier",
+ "plugin:@typescript-eslint/recommended",
+ "plugin:prettier/recommended",
+), {
+ plugins: {
+ "@typescript-eslint": typescriptEslint,
+ },
+
+ languageOptions: {
+ globals: {
+ ...globals.browser,
+ ...globals.amd,
+ ...globals.node,
+ },
+
+ parser: tsParser,
+ },
+
+ rules: {
+ "prettier/prettier": ["error", {
+ singleQuote: true,
+ trailingComma: "all",
+ endOfLine: "auto",
+ arrowParens: "avoid",
+ tabWidth: 2,
+ }],
+
+ "arrow-body-style": "off",
+ "prefer-arrow-callback": "off",
+ "@typescript-eslint/array-type": 0,
+ "@typescript-eslint/no-inferrable-types": "off",
+ "@typescript-eslint/ban-types": "off",
+ "@typescript-eslint/no-unused-vars": "off",
+ "arrow-parens": "off",
+ curly: "off",
+ "no-case-declarations": "off",
+ quotes: "off",
+
+ "@/quotes": ["error", "single", {
+ avoidEscape: true,
+ allowTemplateLiterals: true,
+ }],
+
+ "prefer-rest-params": "off",
+ "no-bitwise": "off",
+ "no-console": "off",
+
+ "no-empty": ["error", {
+ allowEmptyCatch: true,
+ }],
+
+ "@typescript-eslint/no-var-requires": "off",
+ "@typescript-eslint/no-non-null-assertion": "off",
+ "@typescript-eslint/no-explicit-any": "off",
+ "no-unused-expressions": "off",
+ "@typescript-eslint/no-unused-expressions": "off",
+ "@typescript-eslint/no-empty-object-type": "off",
+ "space-before-function-paren": "off",
+ },
+}];
\ No newline at end of file
diff --git a/fixup.cjs b/fixup.cjs
new file mode 100755
index 000000000..98fbe68c3
--- /dev/null
+++ b/fixup.cjs
@@ -0,0 +1,25 @@
+const fs = require('fs');
+const path = require('path');
+
+const updateRequires = (filePath) => {
+ let content = fs.readFileSync(filePath, 'utf8');
+ //replace local imports eg. require("./ecpair.js") to require("ecpair.cjs")
+ content = content.replace(/require\('\.\/([^']*)\.js'\)/g, "require('./$1.cjs')");
+ content = content.replace(/require\('\.\.\/([^']*)\.js'\)/g, "require('../$1.cjs')");
+
+ fs.writeFileSync(filePath, content, 'utf8');
+};
+
+const processFiles = (dir) => {
+ fs.readdirSync(dir).forEach((file) => {
+ const filePath = path.join(dir, file);
+ if (fs.lstatSync(filePath).isDirectory()) {
+ processFiles(filePath);
+ } else if (filePath.endsWith('.cjs')) {
+ updateRequires(filePath);
+ }
+ });
+};
+
+const dir = path.join(__dirname, 'src', 'cjs');
+processFiles(dir);
diff --git a/package-lock.json b/package-lock.json
index c66d2add2..e8eda4bdc 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,845 +1,1574 @@
{
"name": "bitcoinjs-lib",
- "version": "6.0.0",
- "lockfileVersion": 1,
+ "version": "7.0.0",
+ "lockfileVersion": 3,
"requires": true,
- "dependencies": {
- "@babel/code-frame": {
- "version": "7.15.8",
- "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.15.8.tgz",
- "integrity": "sha512-2IAnmn8zbvC/jKYhq5Ki9I+DwjlrtMPUCH/CpHvqI4dNnlwHwsxoIhlc8WcYY5LSYknXQtAlFYuHfqAFCvQ4Wg==",
- "dev": true,
- "requires": {
- "@babel/highlight": "^7.14.5"
- }
- },
- "@babel/core": {
- "version": "7.12.13",
- "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.13.tgz",
- "integrity": "sha512-BQKE9kXkPlXHPeqissfxo0lySWJcYdEP0hdtJOH/iJfDdhOCcgtNCjftCJg3qqauB4h+lz2N6ixM++b9DN1Tcw==",
- "dev": true,
- "requires": {
- "@babel/code-frame": "^7.12.13",
- "@babel/generator": "^7.12.13",
- "@babel/helper-module-transforms": "^7.12.13",
- "@babel/helpers": "^7.12.13",
- "@babel/parser": "^7.12.13",
- "@babel/template": "^7.12.13",
- "@babel/traverse": "^7.12.13",
- "@babel/types": "^7.12.13",
- "convert-source-map": "^1.7.0",
- "debug": "^4.1.0",
- "gensync": "^1.0.0-beta.1",
- "json5": "^2.1.2",
- "lodash": "^4.17.19",
- "semver": "^5.4.1",
- "source-map": "^0.5.0"
- },
- "dependencies": {
- "@babel/code-frame": {
- "version": "7.12.13",
- "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz",
- "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==",
- "dev": true,
- "requires": {
- "@babel/highlight": "^7.12.13"
- }
- },
- "@babel/highlight": {
- "version": "7.12.13",
- "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.12.13.tgz",
- "integrity": "sha512-kocDQvIbgMKlWxXe9fof3TQ+gkIPOUSEYhJjqUjvKMez3krV7vbzYCDq39Oj11UAVK7JqPVGQPlgE85dPNlQww==",
- "dev": true,
- "requires": {
- "@babel/helper-validator-identifier": "^7.12.11",
- "chalk": "^2.0.0",
- "js-tokens": "^4.0.0"
- }
- },
- "debug": {
- "version": "4.3.1",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
- "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
- "dev": true,
- "requires": {
- "ms": "2.1.2"
- }
- },
- "ms": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
- "dev": true
- }
+ "packages": {
+ "": {
+ "name": "bitcoinjs-lib",
+ "version": "7.0.0",
+ "license": "MIT",
+ "dependencies": {
+ "@noble/hashes": "^1.2.0",
+ "bech32": "^2.0.0",
+ "bip174": "^3.0.0",
+ "bs58check": "^4.0.0",
+ "uint8array-tools": "^0.0.9",
+ "valibot": "^0.38.0",
+ "varuint-bitcoin": "^2.0.0"
+ },
+ "devDependencies": {
+ "@eslint/eslintrc": "^3.1.0",
+ "@eslint/js": "^9.9.1",
+ "@types/bs58": "^4.0.0",
+ "@types/bs58check": "^2.1.0",
+ "@types/mocha": "^5.2.7",
+ "@types/node": "^18.7.14",
+ "@types/proxyquire": "^1.3.28",
+ "@types/randombytes": "^2.0.0",
+ "@typescript-eslint/eslint-plugin": "^8.2.0",
+ "@typescript-eslint/parser": "^8.2.0",
+ "better-npm-audit": "^3.7.3",
+ "bip32": "^5.0.0-rc.0",
+ "bip39": "^3.1.0",
+ "bip65": "^1.0.1",
+ "bip68": "^1.0.3",
+ "bitcoinjs-lib": ".",
+ "bs58": "^6.0.0",
+ "c8": "^10.1.2",
+ "dhttp": "^3.0.0",
+ "ecpair": "^3.0.0-rc.0",
+ "eslint": "^9.9.0",
+ "eslint-config-prettier": "^9.1.0",
+ "eslint-plugin-prettier": "^5.2.1",
+ "globals": "^15.9.0",
+ "hoodwink": "^2.0.0",
+ "minimaldata": "^1.0.2",
+ "mocha": "^10.6.0",
+ "prettier": "^3.0.0",
+ "proxyquire": "^2.0.1",
+ "randombytes": "^2.1.0",
+ "regtest-client": "0.2.0",
+ "rimraf": "^2.6.3",
+ "tiny-secp256k1": "^2.2.0",
+ "tsx": "^4.17.0",
+ "typedoc": "^0.26.6",
+ "typescript": "^5.0.4"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@bcoe/v8-coverage": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz",
+ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==",
+ "dev": true
+ },
+ "node_modules/@esbuild/aix-ppc64": {
+ "version": "0.25.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.0.tgz",
+ "integrity": "sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm": {
+ "version": "0.25.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.0.tgz",
+ "integrity": "sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm64": {
+ "version": "0.25.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.0.tgz",
+ "integrity": "sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-x64": {
+ "version": "0.25.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.0.tgz",
+ "integrity": "sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-arm64": {
+ "version": "0.25.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.0.tgz",
+ "integrity": "sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-x64": {
+ "version": "0.25.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.0.tgz",
+ "integrity": "sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.25.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.0.tgz",
+ "integrity": "sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-x64": {
+ "version": "0.25.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.0.tgz",
+ "integrity": "sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm": {
+ "version": "0.25.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.0.tgz",
+ "integrity": "sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm64": {
+ "version": "0.25.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.0.tgz",
+ "integrity": "sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ia32": {
+ "version": "0.25.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.0.tgz",
+ "integrity": "sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-loong64": {
+ "version": "0.25.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.0.tgz",
+ "integrity": "sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-mips64el": {
+ "version": "0.25.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.0.tgz",
+ "integrity": "sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ppc64": {
+ "version": "0.25.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.0.tgz",
+ "integrity": "sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-riscv64": {
+ "version": "0.25.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.0.tgz",
+ "integrity": "sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-s390x": {
+ "version": "0.25.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.0.tgz",
+ "integrity": "sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-x64": {
+ "version": "0.25.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.0.tgz",
+ "integrity": "sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-arm64": {
+ "version": "0.25.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.0.tgz",
+ "integrity": "sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-x64": {
+ "version": "0.25.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.0.tgz",
+ "integrity": "sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-arm64": {
+ "version": "0.25.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.0.tgz",
+ "integrity": "sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-x64": {
+ "version": "0.25.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.0.tgz",
+ "integrity": "sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/sunos-x64": {
+ "version": "0.25.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.0.tgz",
+ "integrity": "sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-arm64": {
+ "version": "0.25.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.0.tgz",
+ "integrity": "sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-ia32": {
+ "version": "0.25.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.0.tgz",
+ "integrity": "sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-x64": {
+ "version": "0.25.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.0.tgz",
+ "integrity": "sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@eslint-community/eslint-utils": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
+ "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==",
+ "dev": true,
+ "dependencies": {
+ "eslint-visitor-keys": "^3.3.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "peerDependencies": {
+ "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
+ }
+ },
+ "node_modules/@eslint-community/regexpp": {
+ "version": "4.11.0",
+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.0.tgz",
+ "integrity": "sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==",
+ "dev": true,
+ "engines": {
+ "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
}
},
- "@babel/generator": {
- "version": "7.12.15",
- "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.15.tgz",
- "integrity": "sha512-6F2xHxBiFXWNSGb7vyCUTBF8RCLY66rS0zEPcP8t/nQyXjha5EuK4z7H5o7fWG8B4M7y6mqVWq1J+1PuwRhecQ==",
+ "node_modules/@eslint/config-array": {
+ "version": "0.18.0",
+ "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.18.0.tgz",
+ "integrity": "sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw==",
"dev": true,
- "requires": {
- "@babel/types": "^7.12.13",
- "jsesc": "^2.5.1",
- "source-map": "^0.5.0"
+ "dependencies": {
+ "@eslint/object-schema": "^2.1.4",
+ "debug": "^4.3.1",
+ "minimatch": "^3.1.2"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
- "@babel/helper-function-name": {
- "version": "7.12.13",
- "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.13.tgz",
- "integrity": "sha512-TZvmPn0UOqmvi5G4vvw0qZTpVptGkB1GL61R6lKvrSdIxGm5Pky7Q3fpKiIkQCAtRCBUwB0PaThlx9vebCDSwA==",
+ "node_modules/@eslint/eslintrc": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz",
+ "integrity": "sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==",
"dev": true,
- "requires": {
- "@babel/helper-get-function-arity": "^7.12.13",
- "@babel/template": "^7.12.13",
- "@babel/types": "^7.12.13"
+ "dependencies": {
+ "ajv": "^6.12.4",
+ "debug": "^4.3.2",
+ "espree": "^10.0.1",
+ "globals": "^14.0.0",
+ "ignore": "^5.2.0",
+ "import-fresh": "^3.2.1",
+ "js-yaml": "^4.1.0",
+ "minimatch": "^3.1.2",
+ "strip-json-comments": "^3.1.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
}
},
- "@babel/helper-get-function-arity": {
- "version": "7.12.13",
- "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz",
- "integrity": "sha512-DjEVzQNz5LICkzN0REdpD5prGoidvbdYk1BVgRUOINaWJP2t6avB27X1guXK1kXNrX0WMfsrm1A/ZBthYuIMQg==",
+ "node_modules/@eslint/eslintrc/node_modules/globals": {
+ "version": "14.0.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
+ "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
"dev": true,
- "requires": {
- "@babel/types": "^7.12.13"
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "@babel/helper-member-expression-to-functions": {
- "version": "7.12.13",
- "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.13.tgz",
- "integrity": "sha512-B+7nN0gIL8FZ8SvMcF+EPyB21KnCcZHQZFczCxbiNGV/O0rsrSBlWGLzmtBJ3GMjSVMIm4lpFhR+VdVBuIsUcQ==",
+ "node_modules/@eslint/js": {
+ "version": "9.9.1",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.9.1.tgz",
+ "integrity": "sha512-xIDQRsfg5hNBqHz04H1R3scSVwmI+KUbqjsQKHKQ1DAUSaUjYPReZZmS/5PNiKu1fUvzDd6H7DEDKACSEhu+TQ==",
"dev": true,
- "requires": {
- "@babel/types": "^7.12.13"
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
- "@babel/helper-module-imports": {
- "version": "7.12.13",
- "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.13.tgz",
- "integrity": "sha512-NGmfvRp9Rqxy0uHSSVP+SRIW1q31a7Ji10cLBcqSDUngGentY4FRiHOFZFE1CLU5eiL0oE8reH7Tg1y99TDM/g==",
+ "node_modules/@eslint/object-schema": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.4.tgz",
+ "integrity": "sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==",
"dev": true,
- "requires": {
- "@babel/types": "^7.12.13"
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
- "@babel/helper-module-transforms": {
- "version": "7.12.13",
- "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.12.13.tgz",
- "integrity": "sha512-acKF7EjqOR67ASIlDTupwkKM1eUisNAjaSduo5Cz+793ikfnpe7p4Q7B7EWU2PCoSTPWsQkR7hRUWEIZPiVLGA==",
+ "node_modules/@humanwhocodes/module-importer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+ "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
"dev": true,
- "requires": {
- "@babel/helper-module-imports": "^7.12.13",
- "@babel/helper-replace-supers": "^7.12.13",
- "@babel/helper-simple-access": "^7.12.13",
- "@babel/helper-split-export-declaration": "^7.12.13",
- "@babel/helper-validator-identifier": "^7.12.11",
- "@babel/template": "^7.12.13",
- "@babel/traverse": "^7.12.13",
- "@babel/types": "^7.12.13",
- "lodash": "^4.17.19"
+ "engines": {
+ "node": ">=12.22"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
}
},
- "@babel/helper-optimise-call-expression": {
- "version": "7.12.13",
- "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.13.tgz",
- "integrity": "sha512-BdWQhoVJkp6nVjB7nkFWcn43dkprYauqtk++Py2eaf/GRDFm5BxRqEIZCiHlZUGAVmtwKcsVL1dC68WmzeFmiA==",
+ "node_modules/@humanwhocodes/retry": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.0.tgz",
+ "integrity": "sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==",
"dev": true,
- "requires": {
- "@babel/types": "^7.12.13"
+ "engines": {
+ "node": ">=18.18"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
}
},
- "@babel/helper-replace-supers": {
- "version": "7.12.13",
- "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.12.13.tgz",
- "integrity": "sha512-pctAOIAMVStI2TMLhozPKbf5yTEXc0OJa0eENheb4w09SrgOWEs+P4nTOZYJQCqs8JlErGLDPDJTiGIp3ygbLg==",
+ "node_modules/@isaacs/cliui": {
+ "version": "8.0.2",
+ "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
+ "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
"dev": true,
- "requires": {
- "@babel/helper-member-expression-to-functions": "^7.12.13",
- "@babel/helper-optimise-call-expression": "^7.12.13",
- "@babel/traverse": "^7.12.13",
- "@babel/types": "^7.12.13"
+ "dependencies": {
+ "string-width": "^5.1.2",
+ "string-width-cjs": "npm:string-width@^4.2.0",
+ "strip-ansi": "^7.0.1",
+ "strip-ansi-cjs": "npm:strip-ansi@^6.0.1",
+ "wrap-ansi": "^8.1.0",
+ "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0"
+ },
+ "engines": {
+ "node": ">=12"
}
},
- "@babel/helper-simple-access": {
- "version": "7.12.13",
- "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.12.13.tgz",
- "integrity": "sha512-0ski5dyYIHEfwpWGx5GPWhH35j342JaflmCeQmsPWcrOQDtCN6C1zKAVRFVbK53lPW2c9TsuLLSUDf0tIGJ5hA==",
+ "node_modules/@isaacs/cliui/node_modules/ansi-regex": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
+ "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
"dev": true,
- "requires": {
- "@babel/types": "^7.12.13"
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
}
},
- "@babel/helper-split-export-declaration": {
- "version": "7.12.13",
- "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.13.tgz",
- "integrity": "sha512-tCJDltF83htUtXx5NLcaDqRmknv652ZWCHyoTETf1CXYJdPC7nohZohjUgieXhv0hTJdRf2FjDueFehdNucpzg==",
+ "node_modules/@isaacs/cliui/node_modules/ansi-styles": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
+ "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
"dev": true,
- "requires": {
- "@babel/types": "^7.12.13"
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
- "@babel/helper-validator-identifier": {
- "version": "7.12.11",
- "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz",
- "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==",
+ "node_modules/@isaacs/cliui/node_modules/emoji-regex": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
+ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
"dev": true
},
- "@babel/helpers": {
- "version": "7.12.13",
- "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.12.13.tgz",
- "integrity": "sha512-oohVzLRZ3GQEk4Cjhfs9YkJA4TdIDTObdBEZGrd6F/T0GPSnuV6l22eMcxlvcvzVIPH3VTtxbseudM1zIE+rPQ==",
+ "node_modules/@isaacs/cliui/node_modules/string-width": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
+ "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
"dev": true,
- "requires": {
- "@babel/template": "^7.12.13",
- "@babel/traverse": "^7.12.13",
- "@babel/types": "^7.12.13"
+ "dependencies": {
+ "eastasianwidth": "^0.2.0",
+ "emoji-regex": "^9.2.2",
+ "strip-ansi": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "@babel/highlight": {
- "version": "7.14.5",
- "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz",
- "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==",
+ "node_modules/@isaacs/cliui/node_modules/strip-ansi": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
+ "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
"dev": true,
- "requires": {
- "@babel/helper-validator-identifier": "^7.14.5",
- "chalk": "^2.0.0",
- "js-tokens": "^4.0.0"
+ "dependencies": {
+ "ansi-regex": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=12"
},
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
+ "node_modules/@isaacs/cliui/node_modules/wrap-ansi": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
+ "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
+ "dev": true,
"dependencies": {
- "@babel/helper-validator-identifier": {
- "version": "7.15.7",
- "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz",
- "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==",
- "dev": true
- }
+ "ansi-styles": "^6.1.0",
+ "string-width": "^5.0.1",
+ "strip-ansi": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/@istanbuljs/schema": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz",
+ "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.0.0"
}
},
- "@babel/parser": {
- "version": "7.12.15",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.15.tgz",
- "integrity": "sha512-AQBOU2Z9kWwSZMd6lNjCX0GUgFonL1wAM1db8L8PMk9UDaGsRCArBkU4Sc+UCM3AE4hjbXx+h58Lb3QT4oRmrA==",
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
+ "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==",
"dev": true
},
- "@babel/template": {
- "version": "7.12.13",
- "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.13.tgz",
- "integrity": "sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA==",
- "dev": true,
- "requires": {
- "@babel/code-frame": "^7.12.13",
- "@babel/parser": "^7.12.13",
- "@babel/types": "^7.12.13"
- },
- "dependencies": {
- "@babel/code-frame": {
- "version": "7.12.13",
- "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz",
- "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==",
- "dev": true,
- "requires": {
- "@babel/highlight": "^7.12.13"
- }
- },
- "@babel/highlight": {
- "version": "7.12.13",
- "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.12.13.tgz",
- "integrity": "sha512-kocDQvIbgMKlWxXe9fof3TQ+gkIPOUSEYhJjqUjvKMez3krV7vbzYCDq39Oj11UAVK7JqPVGQPlgE85dPNlQww==",
- "dev": true,
- "requires": {
- "@babel/helper-validator-identifier": "^7.12.11",
- "chalk": "^2.0.0",
- "js-tokens": "^4.0.0"
- }
- }
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.25",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
+ "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
}
},
- "@babel/traverse": {
- "version": "7.12.13",
- "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.13.tgz",
- "integrity": "sha512-3Zb4w7eE/OslI0fTp8c7b286/cQps3+vdLW3UcwC8VSJC6GbKn55aeVVu2QJNuCDoeKyptLOFrPq8WqZZBodyA==",
- "dev": true,
- "requires": {
- "@babel/code-frame": "^7.12.13",
- "@babel/generator": "^7.12.13",
- "@babel/helper-function-name": "^7.12.13",
- "@babel/helper-split-export-declaration": "^7.12.13",
- "@babel/parser": "^7.12.13",
- "@babel/types": "^7.12.13",
- "debug": "^4.1.0",
- "globals": "^11.1.0",
- "lodash": "^4.17.19"
- },
- "dependencies": {
- "@babel/code-frame": {
- "version": "7.12.13",
- "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz",
- "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==",
- "dev": true,
- "requires": {
- "@babel/highlight": "^7.12.13"
- }
- },
- "@babel/highlight": {
- "version": "7.12.13",
- "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.12.13.tgz",
- "integrity": "sha512-kocDQvIbgMKlWxXe9fof3TQ+gkIPOUSEYhJjqUjvKMez3krV7vbzYCDq39Oj11UAVK7JqPVGQPlgE85dPNlQww==",
- "dev": true,
- "requires": {
- "@babel/helper-validator-identifier": "^7.12.11",
- "chalk": "^2.0.0",
- "js-tokens": "^4.0.0"
- }
- },
- "debug": {
- "version": "4.3.1",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
- "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
- "dev": true,
- "requires": {
- "ms": "2.1.2"
- }
- },
- "ms": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
- "dev": true
- }
+ "node_modules/@noble/hashes": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.5.0.tgz",
+ "integrity": "sha512-1j6kQFb7QRru7eKN3ZDvRcP13rugwdxZqCjbiAVZfIJwgj2A65UmT4TgARXGlXgnRkORLTDTrO19ZErt7+QXgA==",
+ "engines": {
+ "node": "^14.21.3 || >=16"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
}
},
- "@babel/types": {
- "version": "7.12.13",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.13.tgz",
- "integrity": "sha512-oKrdZTld2im1z8bDwTOQvUbxKwE+854zc16qWZQlcTqMN00pWxHQ4ZeOq0yDMnisOpRykH2/5Qqcrk/OlbAjiQ==",
+ "node_modules/@nodelib/fs.scandir": {
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+ "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
"dev": true,
- "requires": {
- "@babel/helper-validator-identifier": "^7.12.11",
- "lodash": "^4.17.19",
- "to-fast-properties": "^2.0.0"
+ "dependencies": {
+ "@nodelib/fs.stat": "2.0.5",
+ "run-parallel": "^1.1.9"
+ },
+ "engines": {
+ "node": ">= 8"
}
},
- "@istanbuljs/load-nyc-config": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz",
- "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==",
+ "node_modules/@nodelib/fs.stat": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+ "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+ "dev": true,
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.walk": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+ "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
"dev": true,
- "requires": {
- "camelcase": "^5.3.1",
- "find-up": "^4.1.0",
- "get-package-type": "^0.1.0",
- "js-yaml": "^3.13.1",
- "resolve-from": "^5.0.0"
- },
"dependencies": {
- "find-up": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
- "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
- "dev": true,
- "requires": {
- "locate-path": "^5.0.0",
- "path-exists": "^4.0.0"
- }
- },
- "locate-path": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
- "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
- "dev": true,
- "requires": {
- "p-locate": "^4.1.0"
- }
- },
- "p-locate": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
- "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
- "dev": true,
- "requires": {
- "p-limit": "^2.2.0"
- }
- },
- "path-exists": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
- "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
- "dev": true
- }
+ "@nodelib/fs.scandir": "2.1.5",
+ "fastq": "^1.6.0"
+ },
+ "engines": {
+ "node": ">= 8"
}
},
- "@istanbuljs/schema": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.2.tgz",
- "integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==",
- "dev": true
+ "node_modules/@pkgjs/parseargs": {
+ "version": "0.11.0",
+ "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
+ "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
+ "dev": true,
+ "optional": true,
+ "engines": {
+ "node": ">=14"
+ }
},
- "@types/base-x": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/@types/base-x/-/base-x-3.0.0.tgz",
- "integrity": "sha512-vnqSlpsv9uFX5/z8GyKWAfWHhLGJDBkrgRRsnxlsX23DHOlNyqP/eHQiv4TwnYcZULzQIxaWA/xRWU9Dyy4qzw==",
+ "node_modules/@pkgr/core": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz",
+ "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==",
"dev": true,
- "requires": {
- "@types/node": "*"
+ "engines": {
+ "node": "^12.20.0 || ^14.18.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/unts"
}
},
- "@types/bs58": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/@types/bs58/-/bs58-4.0.0.tgz",
- "integrity": "sha512-gYX+MHD4G/R+YGYwdhG5gbJj4LsEQGr3Vg6gVDAbe7xC5Bn8dNNG2Lpo6uDX/rT5dE7VBj0rGEFuV8L0AEx4Rg==",
+ "node_modules/@scure/base": {
+ "version": "1.1.8",
+ "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.8.tgz",
+ "integrity": "sha512-6CyAclxj3Nb0XT7GHK6K4zK6k2xJm6E4Ft0Ohjt4WgegiFUHEtFb2CGzmPmGBwoIhrLsqNLYfLr04Y1GePrzZg==",
"dev": true,
- "requires": {
- "@types/base-x": "*"
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
}
},
- "@types/bs58check": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@types/bs58check/-/bs58check-2.1.0.tgz",
- "integrity": "sha512-OxsysnJQh82vy9DRbOcw9m2j/WiyqZLn0YBhKxdQ+aCwoHj+tWzyCgpwAkr79IfDXZKxc6h7k89T9pwS78CqTQ==",
+ "node_modules/@shikijs/core": {
+ "version": "1.16.1",
+ "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-1.16.1.tgz",
+ "integrity": "sha512-aI0hBtw+a6KsJp2jcD4YuQqKpeCbURMZbhHVozDknJpm+KJqeMRkEnfBC8BaKE/5XC+uofPgCLsa/TkTk0Ba0w==",
"dev": true,
- "requires": {
- "@types/node": "*"
+ "dependencies": {
+ "@shikijs/vscode-textmate": "^9.2.0",
+ "@types/hast": "^3.0.4"
+ }
+ },
+ "node_modules/@shikijs/vscode-textmate": {
+ "version": "9.2.0",
+ "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-9.2.0.tgz",
+ "integrity": "sha512-5FinaOp6Vdh/dl4/yaOTh0ZeKch+rYS8DUb38V3GMKYVkdqzxw53lViRKUYkVILRiVQT7dcPC7VvAKOR73zVtQ==",
+ "dev": true
+ },
+ "node_modules/@types/bs58": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/@types/bs58/-/bs58-4.0.4.tgz",
+ "integrity": "sha512-0IEpMFXXQi2zXaXl9GJ3sRwQo0uEkD+yFOv+FnAU5lkPtcu6h61xb7jc2CFPEZ5BUOaiP13ThuGc9HD4R8lR5g==",
+ "dev": true,
+ "dependencies": {
+ "@types/node": "*",
+ "base-x": "^3.0.6"
}
},
- "@types/create-hash": {
- "version": "1.2.2",
- "resolved": "https://registry.npmjs.org/@types/create-hash/-/create-hash-1.2.2.tgz",
- "integrity": "sha512-Fg8/kfMJObbETFU/Tn+Y0jieYewryLrbKwLCEIwPyklZZVY2qB+64KFjhplGSw+cseZosfFXctXO+PyIYD8iZQ==",
+ "node_modules/@types/bs58check": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/@types/bs58check/-/bs58check-2.1.2.tgz",
+ "integrity": "sha512-xpXaQlOIY1KoXlA/ytHGHpEIU87PJt+g9SH7nC6HdCgaBwT2IEZIwBMHbjuX6BpnfbiUMlmwqurdLDwXpcdmSA==",
"dev": true,
- "requires": {
+ "dependencies": {
"@types/node": "*"
}
},
- "@types/mocha": {
+ "node_modules/@types/hast": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz",
+ "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/unist": "*"
+ }
+ },
+ "node_modules/@types/istanbul-lib-coverage": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz",
+ "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==",
+ "dev": true
+ },
+ "node_modules/@types/mocha": {
"version": "5.2.7",
"resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.7.tgz",
"integrity": "sha512-NYrtPht0wGzhwe9+/idPaBB+TqkY9AhTvOLMkThm0IoEfLaiVQZwBwyJ5puCkO3AUCWrmcoePjp2mbFocKy4SQ==",
"dev": true
},
- "@types/node": {
- "version": "16.11.7",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.7.tgz",
- "integrity": "sha512-QB5D2sqfSjCmTuWcBWyJ+/44bcjO7VbjSbOE0ucoVbAsSNQc4Lt6QkgkVXkTDwkL4z/beecZNDvVX15D4P8Jbw==",
- "dev": true
+ "node_modules/@types/node": {
+ "version": "18.19.49",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.49.tgz",
+ "integrity": "sha512-ALCeIR6n0nQ7j0FUF1ycOhrp6+XutJWqEu/vtdEqXFUQwkBfgUA5cEg3ZNmjWGF/ZYA/FcF9QMkL55Ar0O6UrA==",
+ "dev": true,
+ "dependencies": {
+ "undici-types": "~5.26.4"
+ }
},
- "@types/proxyquire": {
- "version": "1.3.28",
- "resolved": "https://registry.npmjs.org/@types/proxyquire/-/proxyquire-1.3.28.tgz",
- "integrity": "sha512-SQaNzWQ2YZSr7FqAyPPiA3FYpux2Lqh3HWMZQk47x3xbMCqgC/w0dY3dw9rGqlweDDkrySQBcaScXWeR+Yb11Q==",
+ "node_modules/@types/proxyquire": {
+ "version": "1.3.31",
+ "resolved": "https://registry.npmjs.org/@types/proxyquire/-/proxyquire-1.3.31.tgz",
+ "integrity": "sha512-uALowNG2TSM1HNPMMOR0AJwv4aPYPhqB0xlEhkeRTMuto5hjoSPZkvgu1nbPUkz3gEPAHv4sy4DmKsurZiEfRQ==",
"dev": true
},
- "@types/randombytes": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/@types/randombytes/-/randombytes-2.0.0.tgz",
- "integrity": "sha512-bz8PhAVlwN72vqefzxa14DKNT8jK/mV66CSjwdVQM/k3Th3EPKfUtdMniwZgMedQTFuywAsfjnZsg+pEnltaMA==",
+ "node_modules/@types/randombytes": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/@types/randombytes/-/randombytes-2.0.3.tgz",
+ "integrity": "sha512-+NRgihTfuURllWCiIAhm1wsJqzsocnqXM77V/CalsdJIYSRGEHMnritxh+6EsBklshC+clo1KgnN14qgSGeQdw==",
"dev": true,
- "requires": {
+ "dependencies": {
"@types/node": "*"
}
},
- "@types/wif": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/@types/wif/-/wif-2.0.2.tgz",
- "integrity": "sha512-IiIuBeJzlh4LWJ7kVTrX0nwB60OG0vvGTaWC/SgSbVFw7uYUTF6gEuvDZ1goWkeirekJDD58Y8g7NljQh2fNkA==",
+ "node_modules/@types/unist": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz",
+ "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==",
+ "dev": true
+ },
+ "node_modules/@typescript-eslint/eslint-plugin": {
+ "version": "8.4.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.4.0.tgz",
+ "integrity": "sha512-rg8LGdv7ri3oAlenMACk9e+AR4wUV0yrrG+XKsGKOK0EVgeEDqurkXMPILG2836fW4ibokTB5v4b6Z9+GYQDEw==",
"dev": true,
- "requires": {
- "@types/node": "*"
+ "dependencies": {
+ "@eslint-community/regexpp": "^4.10.0",
+ "@typescript-eslint/scope-manager": "8.4.0",
+ "@typescript-eslint/type-utils": "8.4.0",
+ "@typescript-eslint/utils": "8.4.0",
+ "@typescript-eslint/visitor-keys": "8.4.0",
+ "graphemer": "^1.4.0",
+ "ignore": "^5.3.1",
+ "natural-compare": "^1.4.0",
+ "ts-api-utils": "^1.3.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0",
+ "eslint": "^8.57.0 || ^9.0.0"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
}
},
- "aggregate-error": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz",
- "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==",
+ "node_modules/@typescript-eslint/parser": {
+ "version": "8.4.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.4.0.tgz",
+ "integrity": "sha512-NHgWmKSgJk5K9N16GIhQ4jSobBoJwrmURaLErad0qlLjrpP5bECYg+wxVTGlGZmJbU03jj/dfnb6V9bw+5icsA==",
"dev": true,
- "requires": {
- "clean-stack": "^2.0.0",
- "indent-string": "^4.0.0"
+ "dependencies": {
+ "@typescript-eslint/scope-manager": "8.4.0",
+ "@typescript-eslint/types": "8.4.0",
+ "@typescript-eslint/typescript-estree": "8.4.0",
+ "@typescript-eslint/visitor-keys": "8.4.0",
+ "debug": "^4.3.4"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
}
},
- "ansi-colors": {
- "version": "3.2.3",
- "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz",
- "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==",
- "dev": true
+ "node_modules/@typescript-eslint/scope-manager": {
+ "version": "8.4.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.4.0.tgz",
+ "integrity": "sha512-n2jFxLeY0JmKfUqy3P70rs6vdoPjHK8P/w+zJcV3fk0b0BwRXC/zxRTEnAsgYT7MwdQDt/ZEbtdzdVC+hcpF0A==",
+ "dev": true,
+ "dependencies": {
+ "@typescript-eslint/types": "8.4.0",
+ "@typescript-eslint/visitor-keys": "8.4.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
},
- "ansi-regex": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
- "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
- "dev": true
+ "node_modules/@typescript-eslint/type-utils": {
+ "version": "8.4.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.4.0.tgz",
+ "integrity": "sha512-pu2PAmNrl9KX6TtirVOrbLPLwDmASpZhK/XU7WvoKoCUkdtq9zF7qQ7gna0GBZFN0hci0vHaSusiL2WpsQk37A==",
+ "dev": true,
+ "dependencies": {
+ "@typescript-eslint/typescript-estree": "8.4.0",
+ "@typescript-eslint/utils": "8.4.0",
+ "debug": "^4.3.4",
+ "ts-api-utils": "^1.3.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
},
- "ansi-styles": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
- "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "node_modules/@typescript-eslint/types": {
+ "version": "8.4.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.4.0.tgz",
+ "integrity": "sha512-T1RB3KQdskh9t3v/qv7niK6P8yvn7ja1mS7QK7XfRVL6wtZ8/mFs/FHf4fKvTA0rKnqnYxl/uHFNbnEt0phgbw==",
"dev": true,
- "requires": {
- "color-convert": "^1.9.0"
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
}
},
- "anymatch": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz",
- "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==",
+ "node_modules/@typescript-eslint/typescript-estree": {
+ "version": "8.4.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.4.0.tgz",
+ "integrity": "sha512-kJ2OIP4dQw5gdI4uXsaxUZHRwWAGpREJ9Zq6D5L0BweyOrWsL6Sz0YcAZGWhvKnH7fm1J5YFE1JrQL0c9dd53A==",
"dev": true,
- "requires": {
- "normalize-path": "^3.0.0",
- "picomatch": "^2.0.4"
+ "dependencies": {
+ "@typescript-eslint/types": "8.4.0",
+ "@typescript-eslint/visitor-keys": "8.4.0",
+ "debug": "^4.3.4",
+ "fast-glob": "^3.3.2",
+ "is-glob": "^4.0.3",
+ "minimatch": "^9.0.4",
+ "semver": "^7.6.0",
+ "ts-api-utils": "^1.3.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
}
},
- "append-transform": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz",
- "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==",
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
"dev": true,
- "requires": {
- "default-require-extensions": "^3.0.0"
+ "dependencies": {
+ "balanced-match": "^1.0.0"
}
},
- "archy": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz",
- "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=",
- "dev": true
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+ "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/@typescript-eslint/utils": {
+ "version": "8.4.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.4.0.tgz",
+ "integrity": "sha512-swULW8n1IKLjRAgciCkTCafyTHHfwVQFt8DovmaF69sKbOxTSFMmIZaSHjqO9i/RV0wIblaawhzvtva8Nmm7lQ==",
+ "dev": true,
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.4.0",
+ "@typescript-eslint/scope-manager": "8.4.0",
+ "@typescript-eslint/types": "8.4.0",
+ "@typescript-eslint/typescript-estree": "8.4.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/visitor-keys": {
+ "version": "8.4.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.4.0.tgz",
+ "integrity": "sha512-zTQD6WLNTre1hj5wp09nBIDiOc2U5r/qmzo7wxPn4ZgAjHql09EofqhF9WF+fZHzL5aCyaIpPcT2hyxl73kr9A==",
+ "dev": true,
+ "dependencies": {
+ "@typescript-eslint/types": "8.4.0",
+ "eslint-visitor-keys": "^3.4.3"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/acorn": {
+ "version": "8.12.1",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz",
+ "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==",
+ "dev": true,
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/acorn-jsx": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+ "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+ "dev": true,
+ "peerDependencies": {
+ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "dev": true,
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/ansi-colors": {
+ "version": "4.1.3",
+ "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz",
+ "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/anymatch": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
+ "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
+ "dev": true,
+ "dependencies": {
+ "normalize-path": "^3.0.0",
+ "picomatch": "^2.0.4"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
},
- "arg": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.1.tgz",
- "integrity": "sha512-SlmP3fEA88MBv0PypnXZ8ZfJhwmDeIE3SP71j37AiXQBXYosPV0x6uISAaHYSlSVhmHOVkomen0tbGk6Anlebw==",
+ "node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
"dev": true
},
- "argparse": {
- "version": "1.0.10",
- "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
- "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+ "node_modules/astral-regex": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz",
+ "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==",
"dev": true,
- "requires": {
- "sprintf-js": "~1.0.2"
+ "engines": {
+ "node": ">=8"
}
},
- "balanced-match": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
- "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
+ "node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"dev": true
},
- "base-x": {
- "version": "3.0.5",
- "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.5.tgz",
- "integrity": "sha512-C3picSgzPSLE+jW3tcBzJoGwitOtazb5B+5YmAxZm2ybmTi9LNgAtDO/jjVEBZwHoXmDBZ9m/IELj3elJVRBcA==",
- "requires": {
+ "node_modules/base-x": {
+ "version": "3.0.10",
+ "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.10.tgz",
+ "integrity": "sha512-7d0s06rR9rYaIWHkpfLIFICM/tkSVdoPC9qYAQRpxn9DdKNWNsKC0uk++akckyLq16Tx2WIinnZ6WRriAt6njQ==",
+ "dev": true,
+ "dependencies": {
"safe-buffer": "^5.0.1"
}
},
- "bech32": {
+ "node_modules/bech32": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/bech32/-/bech32-2.0.0.tgz",
"integrity": "sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg=="
},
- "binary-extensions": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.0.0.tgz",
- "integrity": "sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==",
- "dev": true
+ "node_modules/better-npm-audit": {
+ "version": "3.10.0",
+ "resolved": "https://registry.npmjs.org/better-npm-audit/-/better-npm-audit-3.10.0.tgz",
+ "integrity": "sha512-fzvUFurO4LFr+8TkxWyeOstUAfK5i8Ke0U0Y51N/r2NdEfiSI4YHbWi7YXt+QBJ4jX+dx/cP8ujZ/T5Tl2r4Iw==",
+ "dev": true,
+ "dependencies": {
+ "commander": "^8.0.0",
+ "dayjs": "^1.10.6",
+ "lodash.get": "^4.4.2",
+ "semver": "^7.6.3",
+ "table": "^6.7.1"
+ },
+ "bin": {
+ "better-npm-audit": "index.js"
+ },
+ "engines": {
+ "node": ">= 8.12"
+ }
},
- "bindings": {
- "version": "1.5.0",
- "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
- "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
+ "node_modules/binary-extensions": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
+ "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
"dev": true,
- "requires": {
- "file-uri-to-path": "1.0.0"
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "bip174": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/bip174/-/bip174-2.0.1.tgz",
- "integrity": "sha512-i3X26uKJOkDTAalYAp0Er+qGMDhrbbh2o93/xiPyAN2s25KrClSpe3VXo/7mNJoqA5qfko8rLS2l3RWZgYmjKQ=="
+ "node_modules/bip174": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/bip174/-/bip174-3.0.0.tgz",
+ "integrity": "sha512-N3vz3rqikLEu0d6yQL8GTrSkpYb35NQKWMR7Hlza0lOj6ZOlvQ3Xr7N9Y+JPebaCVoEUHdBeBSuLxcHr71r+Lw==",
+ "license": "MIT",
+ "dependencies": {
+ "uint8array-tools": "^0.0.9",
+ "varuint-bitcoin": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
},
- "bip32": {
- "version": "2.0.6",
- "resolved": "https://registry.npmjs.org/bip32/-/bip32-2.0.6.tgz",
- "integrity": "sha512-HpV5OMLLGTjSVblmrtYRfFFKuQB+GArM0+XP8HGWfJ5vxYBqo+DesvJwOdC2WJ3bCkZShGf0QIfoIpeomVzVdA==",
- "dev": true,
- "requires": {
- "@types/node": "10.12.18",
- "bs58check": "^2.1.1",
- "create-hash": "^1.2.0",
- "create-hmac": "^1.1.7",
- "tiny-secp256k1": "^1.1.3",
- "typeforce": "^1.11.5",
- "wif": "^2.0.6"
- },
- "dependencies": {
- "@types/node": {
- "version": "10.12.18",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.18.tgz",
- "integrity": "sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ==",
- "dev": true
- }
+ "node_modules/bip32": {
+ "version": "5.0.0-rc.0",
+ "resolved": "https://registry.npmjs.org/bip32/-/bip32-5.0.0-rc.0.tgz",
+ "integrity": "sha512-5hVFGrdCnF8GB1Lj2eEo4PRE7+jp+3xBLnfNjydivOkMvKmUKeJ9GG8uOy8prmWl3Oh154uzgfudR1FRkNBudA==",
+ "dev": true,
+ "dependencies": {
+ "@noble/hashes": "^1.2.0",
+ "@scure/base": "^1.1.1",
+ "uint8array-tools": "^0.0.8",
+ "valibot": "^0.37.0",
+ "wif": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18.0.0"
}
},
- "bip39": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/bip39/-/bip39-3.0.2.tgz",
- "integrity": "sha512-J4E1r2N0tUylTKt07ibXvhpT2c5pyAFgvuA5q1H9uDy6dEGpjV8jmymh3MTYJDLCNbIVClSB9FbND49I6N24MQ==",
+ "node_modules/bip32/node_modules/uint8array-tools": {
+ "version": "0.0.8",
+ "resolved": "https://registry.npmjs.org/uint8array-tools/-/uint8array-tools-0.0.8.tgz",
+ "integrity": "sha512-xS6+s8e0Xbx++5/0L+yyexukU7pz//Yg6IHg3BKhXotg1JcYtgxVcUctQ0HxLByiJzpAkNFawz1Nz5Xadzo82g==",
"dev": true,
- "requires": {
- "@types/node": "11.11.6",
- "create-hash": "^1.1.0",
- "pbkdf2": "^3.0.9",
- "randombytes": "^2.0.1"
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/bip32/node_modules/valibot": {
+ "version": "0.37.0",
+ "resolved": "https://registry.npmjs.org/valibot/-/valibot-0.37.0.tgz",
+ "integrity": "sha512-FQz52I8RXgFgOHym3XHYSREbNtkgSjF9prvMFH1nBsRyfL6SfCzoT1GuSDTlbsuPubM7/6Kbw0ZMQb8A+V+VsQ==",
+ "dev": true,
+ "peerDependencies": {
+ "typescript": ">=5"
},
- "dependencies": {
- "@types/node": {
- "version": "11.11.6",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-11.11.6.tgz",
- "integrity": "sha512-Exw4yUWMBXM3X+8oqzJNRqZSwUAaS4+7NdvHqQuFi/d+synz++xmX3QIf+BFqneW8N31R8Ky+sikfZUXq07ggQ==",
- "dev": true
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
}
}
},
- "bip65": {
+ "node_modules/bip39": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/bip39/-/bip39-3.1.0.tgz",
+ "integrity": "sha512-c9kiwdk45Do5GL0vJMe7tS95VjCii65mYAH7DfWl3uW8AVzXKQVUm64i3hzVybBDMp9r7j9iNxR85+ul8MdN/A==",
+ "dev": true,
+ "dependencies": {
+ "@noble/hashes": "^1.2.0"
+ }
+ },
+ "node_modules/bip65": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/bip65/-/bip65-1.0.3.tgz",
"integrity": "sha512-RQ1nc7xtnLa5XltnCqkoR2zmhuz498RjMJwrLKQzOE049D1HUqnYfon7cVSbwS5UGm0/EQlC2CH+NY3MyITA4Q==",
- "dev": true
+ "dev": true,
+ "engines": {
+ "node": ">=4.5.0"
+ }
},
- "bip68": {
+ "node_modules/bip68": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/bip68/-/bip68-1.0.4.tgz",
"integrity": "sha512-O1htyufFTYy3EO0JkHg2CLykdXEtV2ssqw47Gq9A0WByp662xpJnMEB9m43LZjsSDjIAOozWRExlFQk2hlV1XQ==",
- "dev": true
+ "dev": true,
+ "engines": {
+ "node": ">=4.5.0"
+ }
},
- "bitcoin-ops": {
+ "node_modules/bitcoin-ops": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/bitcoin-ops/-/bitcoin-ops-1.4.1.tgz",
"integrity": "sha512-pef6gxZFztEhaE9RY9HmWVmiIHqCb2OyS4HPKkpc6CIiiOa3Qmuoylxc5P2EkU3w+5eTSifI9SEZC88idAIGow==",
"dev": true
},
- "bn.js": {
- "version": "4.11.8",
- "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz",
- "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==",
- "dev": true
+ "node_modules/bitcoinjs-lib": {
+ "resolved": "",
+ "link": true
},
- "brace-expansion": {
+ "node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"dev": true,
- "requires": {
+ "dependencies": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
}
},
- "braces": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
- "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+ "node_modules/braces": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
+ "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
"dev": true,
- "requires": {
- "fill-range": "^7.0.1"
+ "dependencies": {
+ "fill-range": "^7.1.1"
+ },
+ "engines": {
+ "node": ">=8"
}
},
- "brorand": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz",
- "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=",
- "dev": true
- },
- "browser-stdout": {
+ "node_modules/browser-stdout": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz",
"integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==",
"dev": true
},
- "bs58": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz",
- "integrity": "sha1-vhYedsNU9veIrkBx9j806MTwpCo=",
- "requires": {
- "base-x": "^3.0.2"
+ "node_modules/bs58": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/bs58/-/bs58-6.0.0.tgz",
+ "integrity": "sha512-PD0wEnEYg6ijszw/u8s+iI3H17cTymlrwkKhDhPZq+Sokl3AU4htyBFTjAeNAlCCmg0f53g6ih3jATyCKftTfw==",
+ "dependencies": {
+ "base-x": "^5.0.0"
}
},
- "bs58check": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/bs58check/-/bs58check-2.1.2.tgz",
- "integrity": "sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA==",
- "requires": {
- "bs58": "^4.0.0",
- "create-hash": "^1.1.0",
- "safe-buffer": "^5.1.2"
- }
- },
- "buffer-from": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
- "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
- "dev": true
- },
- "builtin-modules": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz",
- "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=",
- "dev": true
+ "node_modules/bs58/node_modules/base-x": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/base-x/-/base-x-5.0.0.tgz",
+ "integrity": "sha512-sMW3VGSX1QWVFA6l8U62MLKz29rRfpTlYdCqLdpLo1/Yd4zZwSbnUaDfciIAowAqvq7YFnWq9hrhdg1KYgc1lQ=="
},
- "caching-transform": {
+ "node_modules/bs58check": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz",
- "integrity": "sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==",
+ "resolved": "https://registry.npmjs.org/bs58check/-/bs58check-4.0.0.tgz",
+ "integrity": "sha512-FsGDOnFg9aVI9erdriULkd/JjEWONV/lQE5aYziB5PoBsXRind56lh8doIZIc9X4HoxT5x4bLjMWN1/NB8Zp5g==",
+ "dependencies": {
+ "@noble/hashes": "^1.2.0",
+ "bs58": "^6.0.0"
+ }
+ },
+ "node_modules/c8": {
+ "version": "10.1.2",
+ "resolved": "https://registry.npmjs.org/c8/-/c8-10.1.2.tgz",
+ "integrity": "sha512-Qr6rj76eSshu5CgRYvktW0uM0CFY0yi4Fd5D0duDXO6sYinyopmftUiJVuzBQxQcwQLor7JWDVRP+dUfCmzgJw==",
"dev": true,
- "requires": {
- "hasha": "^5.0.0",
- "make-dir": "^3.0.0",
- "package-hash": "^4.0.0",
- "write-file-atomic": "^3.0.0"
+ "dependencies": {
+ "@bcoe/v8-coverage": "^0.2.3",
+ "@istanbuljs/schema": "^0.1.3",
+ "find-up": "^5.0.0",
+ "foreground-child": "^3.1.1",
+ "istanbul-lib-coverage": "^3.2.0",
+ "istanbul-lib-report": "^3.0.1",
+ "istanbul-reports": "^3.1.6",
+ "test-exclude": "^7.0.1",
+ "v8-to-istanbul": "^9.0.0",
+ "yargs": "^17.7.2",
+ "yargs-parser": "^21.1.1"
+ },
+ "bin": {
+ "c8": "bin/c8.js"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "monocart-coverage-reports": "^2"
+ },
+ "peerDependenciesMeta": {
+ "monocart-coverage-reports": {
+ "optional": true
+ }
}
},
- "camelcase": {
- "version": "5.3.1",
- "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
- "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
- "dev": true
+ "node_modules/callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
},
- "chalk": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
- "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+ "node_modules/camelcase": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
+ "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
"dev": true,
- "requires": {
- "ansi-styles": "^3.2.1",
- "escape-string-regexp": "^1.0.5",
- "supports-color": "^5.3.0"
+ "engines": {
+ "node": ">=10"
},
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
"dependencies": {
- "supports-color": {
- "version": "5.5.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
- "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
- "dev": true,
- "requires": {
- "has-flag": "^3.0.0"
- }
- }
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
}
},
- "chokidar": {
- "version": "3.3.0",
- "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.0.tgz",
- "integrity": "sha512-dGmKLDdT3Gdl7fBUe8XK+gAtGmzy5Fn0XkkWQuYxGIgWVPPse2CxFA5mtrlD0TOHaHjEUqkWNyP1XdHoJES/4A==",
+ "node_modules/chokidar": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
+ "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
"dev": true,
- "requires": {
- "anymatch": "~3.1.1",
+ "dependencies": {
+ "anymatch": "~3.1.2",
"braces": "~3.0.2",
- "fsevents": "~2.1.1",
- "glob-parent": "~5.1.0",
+ "glob-parent": "~5.1.2",
"is-binary-path": "~2.1.0",
"is-glob": "~4.0.1",
"normalize-path": "~3.0.0",
- "readdirp": "~3.2.0"
+ "readdirp": "~3.6.0"
+ },
+ "engines": {
+ "node": ">= 8.10.0"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/chokidar/node_modules/glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dev": true,
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
}
},
- "cipher-base": {
+ "node_modules/cipher-base": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz",
"integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==",
- "requires": {
+ "dev": true,
+ "dependencies": {
"inherits": "^2.0.1",
"safe-buffer": "^5.0.1"
}
},
- "clean-stack": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
- "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==",
- "dev": true
- },
- "cliui": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
- "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==",
+ "node_modules/cliui": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
+ "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
"dev": true,
- "requires": {
- "string-width": "^3.1.0",
- "strip-ansi": "^5.2.0",
- "wrap-ansi": "^5.1.0"
- },
"dependencies": {
- "ansi-regex": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
- "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
- "dev": true
- },
- "string-width": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
- "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
- "dev": true,
- "requires": {
- "emoji-regex": "^7.0.1",
- "is-fullwidth-code-point": "^2.0.0",
- "strip-ansi": "^5.1.0"
- }
- },
- "strip-ansi": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
- "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
- "dev": true,
- "requires": {
- "ansi-regex": "^4.1.0"
- }
- }
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.1",
+ "wrap-ansi": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=12"
}
},
- "color-convert": {
- "version": "1.9.3",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
- "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
- "requires": {
- "color-name": "1.1.3"
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
}
},
- "color-name": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
- "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
- "dev": true
- },
- "commander": {
- "version": "2.20.3",
- "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
- "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
},
- "commondir": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
- "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=",
- "dev": true
+ "node_modules/commander": {
+ "version": "8.3.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz",
+ "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==",
+ "dev": true,
+ "engines": {
+ "node": ">= 12"
+ }
},
- "concat-map": {
+ "node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
- "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
"dev": true
},
- "convert-source-map": {
- "version": "1.7.0",
- "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz",
- "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==",
- "dev": true,
- "requires": {
- "safe-buffer": "~5.1.1"
- }
+ "node_modules/convert-source-map": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
+ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
+ "dev": true
},
- "create-hash": {
+ "node_modules/create-hash": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz",
"integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==",
- "requires": {
+ "dev": true,
+ "dependencies": {
"cipher-base": "^1.0.1",
"inherits": "^2.0.1",
"md5.js": "^1.3.4",
@@ -847,1923 +1576,2485 @@
"sha.js": "^2.4.0"
}
},
- "create-hmac": {
- "version": "1.1.7",
- "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz",
- "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==",
- "dev": true,
- "requires": {
- "cipher-base": "^1.0.3",
- "create-hash": "^1.1.0",
- "inherits": "^2.0.1",
- "ripemd160": "^2.0.0",
- "safe-buffer": "^5.0.1",
- "sha.js": "^2.4.8"
- }
- },
- "cross-spawn": {
- "version": "7.0.3",
- "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
- "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+ "node_modules/cross-spawn": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
+ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
"dev": true,
- "requires": {
+ "license": "MIT",
+ "dependencies": {
"path-key": "^3.1.0",
"shebang-command": "^2.0.0",
"which": "^2.0.1"
},
- "dependencies": {
- "which": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
- "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
- "dev": true,
- "requires": {
- "isexe": "^2.0.0"
- }
- }
- }
- },
- "debug": {
- "version": "3.2.6",
- "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
- "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
- "dev": true,
- "requires": {
- "ms": "^2.1.1"
+ "engines": {
+ "node": ">= 8"
}
},
- "decamelize": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
- "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
+ "node_modules/dayjs": {
+ "version": "1.11.13",
+ "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz",
+ "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==",
"dev": true
},
- "default-require-extensions": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.0.tgz",
- "integrity": "sha512-ek6DpXq/SCpvjhpFsLFRVtIxJCRw6fUR42lYMVZuUMK7n8eMz4Uh5clckdBjEpLhn/gEBZo7hDJnJcwdKLKQjg==",
+ "node_modules/debug": {
+ "version": "4.3.6",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz",
+ "integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==",
"dev": true,
- "requires": {
- "strip-bom": "^4.0.0"
+ "dependencies": {
+ "ms": "2.1.2"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
}
},
- "define-properties": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
- "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
+ "node_modules/decamelize": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz",
+ "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==",
"dev": true,
- "requires": {
- "object-keys": "^1.0.12"
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "dhttp": {
+ "node_modules/deep-is": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
+ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+ "dev": true
+ },
+ "node_modules/dhttp": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/dhttp/-/dhttp-3.0.3.tgz",
"integrity": "sha512-map1b8iyvxSv0uEw3DUDDK5XvH3aYA7QU9DcXy8e3FBIXSwHPHTZWVrOot7Iu9mieWq5XcrZemEJlob6IdCBmg==",
+ "deprecated": "Not maintained, don't use this",
"dev": true,
- "requires": {
+ "dependencies": {
"statuses": "^1.5.0"
}
},
- "diff": {
- "version": "3.5.0",
- "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz",
- "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==",
+ "node_modules/diff": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz",
+ "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.3.1"
+ }
+ },
+ "node_modules/eastasianwidth": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
+ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
"dev": true
},
- "ecpair": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/ecpair/-/ecpair-1.0.0.tgz",
- "integrity": "sha512-1L+P/ivLC3eKHgqcX1M9tFYQWXDoqwJ3zQnN7zDaTtLpiCQKpFTaAZvnsPC5PkWB4q3EPFAHffCLvjfCqRjuwQ==",
- "dev": true,
- "requires": {
- "randombytes": "^2.0.1",
- "tiny-secp256k1": "^1.1.6",
- "typeforce": "^1.11.3",
- "wif": "^2.0.1"
- }
- },
- "elliptic": {
- "version": "6.5.4",
- "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz",
- "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==",
- "dev": true,
- "requires": {
- "bn.js": "^4.11.9",
- "brorand": "^1.1.0",
- "hash.js": "^1.0.0",
- "hmac-drbg": "^1.0.1",
- "inherits": "^2.0.4",
- "minimalistic-assert": "^1.0.1",
- "minimalistic-crypto-utils": "^1.0.1"
- },
+ "node_modules/ecpair": {
+ "version": "3.0.0-rc.0",
+ "resolved": "https://registry.npmjs.org/ecpair/-/ecpair-3.0.0-rc.0.tgz",
+ "integrity": "sha512-y2CTHgI2MMeYiXd8IFgxgORWR1PpyeUACek4viZm12VcbooWhVnqFEHISO0NAiZKJb7CqXEOz5bnhv/UI0ULzw==",
+ "dev": true,
"dependencies": {
- "bn.js": {
- "version": "4.12.0",
- "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
- "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==",
- "dev": true
- },
- "inherits": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
- "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
- "dev": true
- }
+ "uint8array-tools": "^0.0.8",
+ "valibot": "^0.37.0",
+ "wif": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18.0.0"
}
},
- "emoji-regex": {
- "version": "7.0.3",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
- "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
- "dev": true
+ "node_modules/ecpair/node_modules/uint8array-tools": {
+ "version": "0.0.8",
+ "resolved": "https://registry.npmjs.org/uint8array-tools/-/uint8array-tools-0.0.8.tgz",
+ "integrity": "sha512-xS6+s8e0Xbx++5/0L+yyexukU7pz//Yg6IHg3BKhXotg1JcYtgxVcUctQ0HxLByiJzpAkNFawz1Nz5Xadzo82g==",
+ "dev": true,
+ "engines": {
+ "node": ">=14.0.0"
+ }
},
- "es-abstract": {
- "version": "1.17.4",
- "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.4.tgz",
- "integrity": "sha512-Ae3um/gb8F0mui/jPL+QiqmglkUsaQf7FwBEHYIFkztkneosu9imhqHpBzQ3h1vit8t5iQ74t6PEVvphBZiuiQ==",
- "dev": true,
- "requires": {
- "es-to-primitive": "^1.2.1",
- "function-bind": "^1.1.1",
- "has": "^1.0.3",
- "has-symbols": "^1.0.1",
- "is-callable": "^1.1.5",
- "is-regex": "^1.0.5",
- "object-inspect": "^1.7.0",
- "object-keys": "^1.1.1",
- "object.assign": "^4.1.0",
- "string.prototype.trimleft": "^2.1.1",
- "string.prototype.trimright": "^2.1.1"
- }
- },
- "es-to-primitive": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
- "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
+ "node_modules/ecpair/node_modules/valibot": {
+ "version": "0.37.0",
+ "resolved": "https://registry.npmjs.org/valibot/-/valibot-0.37.0.tgz",
+ "integrity": "sha512-FQz52I8RXgFgOHym3XHYSREbNtkgSjF9prvMFH1nBsRyfL6SfCzoT1GuSDTlbsuPubM7/6Kbw0ZMQb8A+V+VsQ==",
"dev": true,
- "requires": {
- "is-callable": "^1.1.4",
- "is-date-object": "^1.0.1",
- "is-symbol": "^1.0.2"
+ "peerDependencies": {
+ "typescript": ">=5"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
}
},
- "es6-error": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz",
- "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==",
+ "node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"dev": true
},
- "escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
- "dev": true
+ "node_modules/entities": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
+ "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.12"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
},
- "esprima": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
- "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
- "dev": true
+ "node_modules/esbuild": {
+ "version": "0.25.0",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.0.tgz",
+ "integrity": "sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.25.0",
+ "@esbuild/android-arm": "0.25.0",
+ "@esbuild/android-arm64": "0.25.0",
+ "@esbuild/android-x64": "0.25.0",
+ "@esbuild/darwin-arm64": "0.25.0",
+ "@esbuild/darwin-x64": "0.25.0",
+ "@esbuild/freebsd-arm64": "0.25.0",
+ "@esbuild/freebsd-x64": "0.25.0",
+ "@esbuild/linux-arm": "0.25.0",
+ "@esbuild/linux-arm64": "0.25.0",
+ "@esbuild/linux-ia32": "0.25.0",
+ "@esbuild/linux-loong64": "0.25.0",
+ "@esbuild/linux-mips64el": "0.25.0",
+ "@esbuild/linux-ppc64": "0.25.0",
+ "@esbuild/linux-riscv64": "0.25.0",
+ "@esbuild/linux-s390x": "0.25.0",
+ "@esbuild/linux-x64": "0.25.0",
+ "@esbuild/netbsd-arm64": "0.25.0",
+ "@esbuild/netbsd-x64": "0.25.0",
+ "@esbuild/openbsd-arm64": "0.25.0",
+ "@esbuild/openbsd-x64": "0.25.0",
+ "@esbuild/sunos-x64": "0.25.0",
+ "@esbuild/win32-arm64": "0.25.0",
+ "@esbuild/win32-ia32": "0.25.0",
+ "@esbuild/win32-x64": "0.25.0"
+ }
+ },
+ "node_modules/escalade": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
+ "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
},
- "file-uri-to-path": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
- "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
- "dev": true
+ "node_modules/escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
},
- "fill-keys": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/fill-keys/-/fill-keys-1.0.2.tgz",
- "integrity": "sha1-mo+jb06K1jTjv2tPPIiCVRRS6yA=",
+ "node_modules/eslint": {
+ "version": "9.9.1",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.9.1.tgz",
+ "integrity": "sha512-dHvhrbfr4xFQ9/dq+jcVneZMyRYLjggWjk6RVsIiHsP8Rz6yZ8LvZ//iU4TrZF+SXWG+JkNF2OyiZRvzgRDqMg==",
"dev": true,
- "requires": {
- "is-object": "~1.0.1",
- "merge-descriptors": "~1.0.0"
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.2.0",
+ "@eslint-community/regexpp": "^4.11.0",
+ "@eslint/config-array": "^0.18.0",
+ "@eslint/eslintrc": "^3.1.0",
+ "@eslint/js": "9.9.1",
+ "@humanwhocodes/module-importer": "^1.0.1",
+ "@humanwhocodes/retry": "^0.3.0",
+ "@nodelib/fs.walk": "^1.2.8",
+ "ajv": "^6.12.4",
+ "chalk": "^4.0.0",
+ "cross-spawn": "^7.0.2",
+ "debug": "^4.3.2",
+ "escape-string-regexp": "^4.0.0",
+ "eslint-scope": "^8.0.2",
+ "eslint-visitor-keys": "^4.0.0",
+ "espree": "^10.1.0",
+ "esquery": "^1.5.0",
+ "esutils": "^2.0.2",
+ "fast-deep-equal": "^3.1.3",
+ "file-entry-cache": "^8.0.0",
+ "find-up": "^5.0.0",
+ "glob-parent": "^6.0.2",
+ "ignore": "^5.2.0",
+ "imurmurhash": "^0.1.4",
+ "is-glob": "^4.0.0",
+ "is-path-inside": "^3.0.3",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "levn": "^0.4.1",
+ "lodash.merge": "^4.6.2",
+ "minimatch": "^3.1.2",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.9.3",
+ "strip-ansi": "^6.0.1",
+ "text-table": "^0.2.0"
+ },
+ "bin": {
+ "eslint": "bin/eslint.js"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://eslint.org/donate"
+ },
+ "peerDependencies": {
+ "jiti": "*"
+ },
+ "peerDependenciesMeta": {
+ "jiti": {
+ "optional": true
+ }
}
},
- "fill-range": {
- "version": "7.0.1",
- "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
- "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+ "node_modules/eslint-config-prettier": {
+ "version": "9.1.0",
+ "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz",
+ "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==",
"dev": true,
- "requires": {
- "to-regex-range": "^5.0.1"
+ "bin": {
+ "eslint-config-prettier": "bin/cli.js"
+ },
+ "peerDependencies": {
+ "eslint": ">=7.0.0"
}
},
- "find-cache-dir": {
- "version": "3.3.1",
- "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz",
- "integrity": "sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ==",
+ "node_modules/eslint-plugin-prettier": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.1.tgz",
+ "integrity": "sha512-gH3iR3g4JfF+yYPaJYkN7jEl9QbweL/YfkoRlNnuIEHEz1vHVlCmWOS+eGGiRuzHQXdJFCOTxRgvju9b8VUmrw==",
"dev": true,
- "requires": {
- "commondir": "^1.0.1",
- "make-dir": "^3.0.2",
- "pkg-dir": "^4.1.0"
+ "dependencies": {
+ "prettier-linter-helpers": "^1.0.0",
+ "synckit": "^0.9.1"
+ },
+ "engines": {
+ "node": "^14.18.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint-plugin-prettier"
+ },
+ "peerDependencies": {
+ "@types/eslint": ">=8.0.0",
+ "eslint": ">=8.0.0",
+ "eslint-config-prettier": "*",
+ "prettier": ">=3.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/eslint": {
+ "optional": true
+ },
+ "eslint-config-prettier": {
+ "optional": true
+ }
}
},
- "find-up": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
- "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
+ "node_modules/eslint-scope": {
+ "version": "8.0.2",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.0.2.tgz",
+ "integrity": "sha512-6E4xmrTw5wtxnLA5wYL3WDfhZ/1bUBGOXV0zQvVRDOtrR8D0p6W7fs3JweNYhwRYeGvd/1CKX2se0/2s7Q/nJA==",
"dev": true,
- "requires": {
- "locate-path": "^3.0.0"
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
}
},
- "flat": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.0.tgz",
- "integrity": "sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw==",
+ "node_modules/eslint-visitor-keys": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
"dev": true,
- "requires": {
- "is-buffer": "~2.0.3"
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
}
},
- "foreground-child": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz",
- "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==",
+ "node_modules/eslint/node_modules/eslint-visitor-keys": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz",
+ "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==",
"dev": true,
- "requires": {
- "cross-spawn": "^7.0.0",
- "signal-exit": "^3.0.2"
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
}
},
- "fromentries": {
- "version": "1.3.2",
- "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz",
- "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==",
- "dev": true
+ "node_modules/espree": {
+ "version": "10.1.0",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-10.1.0.tgz",
+ "integrity": "sha512-M1M6CpiE6ffoigIOWYO9UDP8TMUw9kqb21tf+08IgDYjCsOvCuDt4jQcZmoYxx+w7zlKw9/N0KXfto+I8/FrXA==",
+ "dev": true,
+ "dependencies": {
+ "acorn": "^8.12.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^4.0.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
},
- "fs.realpath": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
- "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
- "dev": true
+ "node_modules/espree/node_modules/eslint-visitor-keys": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz",
+ "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==",
+ "dev": true,
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
},
- "fsevents": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.2.tgz",
- "integrity": "sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA==",
+ "node_modules/esquery": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz",
+ "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==",
"dev": true,
- "optional": true
+ "dependencies": {
+ "estraverse": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
},
- "function-bind": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
- "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
- "dev": true
+ "node_modules/esrecurse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+ "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+ "dev": true,
+ "dependencies": {
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
},
- "gensync": {
- "version": "1.0.0-beta.2",
- "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
- "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
- "dev": true
+ "node_modules/estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true,
+ "engines": {
+ "node": ">=4.0"
+ }
},
- "get-caller-file": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
- "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+ "node_modules/esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
"dev": true
},
- "get-package-type": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz",
- "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==",
+ "node_modules/fast-diff": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz",
+ "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==",
"dev": true
},
- "glob": {
- "version": "7.1.3",
- "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
- "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
+ "node_modules/fast-glob": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz",
+ "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==",
"dev": true,
- "requires": {
- "fs.realpath": "^1.0.0",
- "inflight": "^1.0.4",
- "inherits": "2",
- "minimatch": "^3.0.4",
- "once": "^1.3.0",
- "path-is-absolute": "^1.0.0"
+ "dependencies": {
+ "@nodelib/fs.stat": "^2.0.2",
+ "@nodelib/fs.walk": "^1.2.3",
+ "glob-parent": "^5.1.2",
+ "merge2": "^1.3.0",
+ "micromatch": "^4.0.4"
+ },
+ "engines": {
+ "node": ">=8.6.0"
}
},
- "glob-parent": {
+ "node_modules/fast-glob/node_modules/glob-parent": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
"dev": true,
- "requires": {
+ "dependencies": {
"is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
}
},
- "globals": {
- "version": "11.12.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
- "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
+ "node_modules/fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
"dev": true
},
- "graceful-fs": {
- "version": "4.2.4",
- "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
- "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==",
+ "node_modules/fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
"dev": true
},
- "growl": {
- "version": "1.10.5",
- "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz",
- "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==",
+ "node_modules/fast-uri": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.1.tgz",
+ "integrity": "sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw==",
"dev": true
},
- "has": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
- "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+ "node_modules/fastq": {
+ "version": "1.17.1",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz",
+ "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==",
"dev": true,
- "requires": {
- "function-bind": "^1.1.1"
+ "dependencies": {
+ "reusify": "^1.0.4"
}
},
- "has-flag": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
- "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
- "dev": true
- },
- "has-symbols": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
- "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==",
- "dev": true
+ "node_modules/file-entry-cache": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
+ "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==",
+ "dev": true,
+ "dependencies": {
+ "flat-cache": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
},
- "hash-base": {
- "version": "3.0.4",
- "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz",
- "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=",
- "requires": {
- "inherits": "^2.0.1",
- "safe-buffer": "^5.0.1"
+ "node_modules/fill-keys": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/fill-keys/-/fill-keys-1.0.2.tgz",
+ "integrity": "sha512-tcgI872xXjwFF4xgQmLxi76GnwJG3g/3isB1l4/G5Z4zrbddGpBjqZCO9oEAcB5wX0Hj/5iQB3toxfO7in1hHA==",
+ "dev": true,
+ "dependencies": {
+ "is-object": "~1.0.1",
+ "merge-descriptors": "~1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
}
},
- "hash.js": {
- "version": "1.1.7",
- "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz",
- "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==",
+ "node_modules/fill-range": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
+ "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
"dev": true,
- "requires": {
- "inherits": "^2.0.3",
- "minimalistic-assert": "^1.0.1"
+ "dependencies": {
+ "to-regex-range": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
}
},
- "hasha": {
- "version": "5.2.2",
- "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz",
- "integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==",
+ "node_modules/find-up": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+ "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
"dev": true,
- "requires": {
- "is-stream": "^2.0.0",
- "type-fest": "^0.8.0"
+ "dependencies": {
+ "locate-path": "^6.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "he": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
- "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
- "dev": true
+ "node_modules/flat": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz",
+ "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==",
+ "dev": true,
+ "bin": {
+ "flat": "cli.js"
+ }
},
- "hmac-drbg": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
- "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=",
+ "node_modules/flat-cache": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
+ "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==",
"dev": true,
- "requires": {
- "hash.js": "^1.0.3",
- "minimalistic-assert": "^1.0.0",
- "minimalistic-crypto-utils": "^1.0.1"
+ "dependencies": {
+ "flatted": "^3.2.9",
+ "keyv": "^4.5.4"
+ },
+ "engines": {
+ "node": ">=16"
}
},
- "hoodwink": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/hoodwink/-/hoodwink-2.0.0.tgz",
- "integrity": "sha512-j1jog3tDfhpWlqbVbh29qc7FG7w+NT4ed+QQFGqvww83+50AzzretB7wykZGOe28mBdvCYH3GdHaVWJQ2lJ/4w==",
+ "node_modules/flatted": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz",
+ "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==",
"dev": true
},
- "html-escaper": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
- "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
- "dev": true
+ "node_modules/foreground-child": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz",
+ "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==",
+ "dev": true,
+ "dependencies": {
+ "cross-spawn": "^7.0.0",
+ "signal-exit": "^4.0.1"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
},
- "imurmurhash": {
- "version": "0.1.4",
- "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
- "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=",
+ "node_modules/fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
"dev": true
},
- "indent-string": {
+ "node_modules/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "dev": true,
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-caller-file": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+ "dev": true,
+ "engines": {
+ "node": "6.* || 8.* || >= 10.*"
+ }
+ },
+ "node_modules/get-tsconfig": {
+ "version": "4.8.0",
+ "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.8.0.tgz",
+ "integrity": "sha512-Pgba6TExTZ0FJAn1qkJAjIeKoDJ3CsI2ChuLohJnZl/tTU8MVrq3b+2t5UOPfRa4RMsorClBjJALkJUMjG1PAw==",
+ "dev": true,
+ "dependencies": {
+ "resolve-pkg-maps": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
+ }
+ },
+ "node_modules/glob": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz",
+ "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==",
+ "deprecated": "Glob versions prior to v9 are no longer supported",
+ "dev": true,
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^5.0.1",
+ "once": "^1.3.0"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/glob-parent": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+ "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+ "dev": true,
+ "dependencies": {
+ "is-glob": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/glob/node_modules/brace-expansion": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/glob/node_modules/minimatch": {
+ "version": "5.1.6",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
+ "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/globals": {
+ "version": "15.9.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-15.9.0.tgz",
+ "integrity": "sha512-SmSKyLLKFbSr6rptvP8izbyxJL4ILwqO9Jg23UA0sDlGlu58V59D1//I3vlc0KJphVdUR7vMjHIplYnzBxorQA==",
+ "dev": true,
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/graphemer": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
+ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
+ "dev": true
+ },
+ "node_modules/has-flag": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
- "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/hash-base": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz",
+ "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==",
+ "dev": true,
+ "dependencies": {
+ "inherits": "^2.0.4",
+ "readable-stream": "^3.6.0",
+ "safe-buffer": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/hasown": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
+ "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+ "dev": true,
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/he": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
+ "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
+ "dev": true,
+ "bin": {
+ "he": "bin/he"
+ }
+ },
+ "node_modules/hoodwink": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/hoodwink/-/hoodwink-2.0.0.tgz",
+ "integrity": "sha512-j1jog3tDfhpWlqbVbh29qc7FG7w+NT4ed+QQFGqvww83+50AzzretB7wykZGOe28mBdvCYH3GdHaVWJQ2lJ/4w==",
"dev": true
},
- "inflight": {
+ "node_modules/html-escaper": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
+ "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
+ "dev": true
+ },
+ "node_modules/ignore": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
+ "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
+ "dev": true,
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/import-fresh": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
+ "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
+ "dev": true,
+ "dependencies": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.8.19"
+ }
+ },
+ "node_modules/inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
- "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+ "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+ "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
"dev": true,
- "requires": {
+ "dependencies": {
"once": "^1.3.0",
"wrappy": "1"
}
},
- "inherits": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
- "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "dev": true
},
- "is-binary-path": {
+ "node_modules/is-binary-path": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
"dev": true,
- "requires": {
+ "dependencies": {
"binary-extensions": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
}
},
- "is-buffer": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz",
- "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==",
- "dev": true
- },
- "is-callable": {
- "version": "1.1.5",
- "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz",
- "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==",
- "dev": true
- },
- "is-date-object": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz",
- "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==",
- "dev": true
+ "node_modules/is-core-module": {
+ "version": "2.15.1",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz",
+ "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==",
+ "dev": true,
+ "dependencies": {
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
},
- "is-extglob": {
+ "node_modules/is-extglob": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
- "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
- "dev": true
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
},
- "is-fullwidth-code-point": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
- "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
- "dev": true
+ "node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
},
- "is-glob": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
- "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
"dev": true,
- "requires": {
+ "dependencies": {
"is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
}
},
- "is-number": {
+ "node_modules/is-number": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
- "dev": true
- },
- "is-object": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/is-object/-/is-object-1.0.1.tgz",
- "integrity": "sha1-iVJojF7C/9awPsyF52ngKQMINHA=",
- "dev": true
- },
- "is-regex": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz",
- "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==",
"dev": true,
- "requires": {
- "has": "^1.0.3"
+ "engines": {
+ "node": ">=0.12.0"
}
},
- "is-stream": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz",
- "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==",
- "dev": true
+ "node_modules/is-object": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-object/-/is-object-1.0.2.tgz",
+ "integrity": "sha512-2rRIahhZr2UWb45fIOuvZGpFtz0TyOZLf32KxBbSoUCeZR495zCKlWUKKUByk3geS2eAs7ZAABt0Y/Rx0GiQGA==",
+ "dev": true,
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
},
- "is-symbol": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz",
- "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==",
+ "node_modules/is-path-inside": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
+ "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
"dev": true,
- "requires": {
- "has-symbols": "^1.0.1"
+ "engines": {
+ "node": ">=8"
}
},
- "is-typedarray": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
- "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=",
- "dev": true
+ "node_modules/is-plain-obj": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz",
+ "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
},
- "is-windows": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz",
- "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==",
- "dev": true
+ "node_modules/is-unicode-supported": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz",
+ "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
},
- "isexe": {
+ "node_modules/isexe": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
- "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
"dev": true
},
- "istanbul-lib-coverage": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz",
- "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==",
- "dev": true
- },
- "istanbul-lib-hook": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz",
- "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==",
+ "node_modules/istanbul-lib-coverage": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz",
+ "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==",
"dev": true,
- "requires": {
- "append-transform": "^2.0.0"
+ "engines": {
+ "node": ">=8"
}
},
- "istanbul-lib-instrument": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz",
- "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==",
+ "node_modules/istanbul-lib-report": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz",
+ "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==",
"dev": true,
- "requires": {
- "@babel/core": "^7.7.5",
- "@istanbuljs/schema": "^0.1.2",
+ "dependencies": {
"istanbul-lib-coverage": "^3.0.0",
- "semver": "^6.3.0"
+ "make-dir": "^4.0.0",
+ "supports-color": "^7.1.0"
},
- "dependencies": {
- "semver": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
- "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
- "dev": true
- }
+ "engines": {
+ "node": ">=10"
}
},
- "istanbul-lib-processinfo": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.2.tgz",
- "integrity": "sha512-kOwpa7z9hme+IBPZMzQ5vdQj8srYgAtaRqeI48NGmAQ+/5yKiHLV0QbYqQpxsdEF0+w14SoB8YbnHKcXE2KnYw==",
+ "node_modules/istanbul-reports": {
+ "version": "3.1.7",
+ "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz",
+ "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==",
"dev": true,
- "requires": {
- "archy": "^1.0.0",
- "cross-spawn": "^7.0.0",
- "istanbul-lib-coverage": "^3.0.0-alpha.1",
- "make-dir": "^3.0.0",
- "p-map": "^3.0.0",
- "rimraf": "^3.0.0",
- "uuid": "^3.3.3"
- },
- "dependencies": {
- "rimraf": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
- "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
- "dev": true,
- "requires": {
- "glob": "^7.1.3"
- }
- }
+ "dependencies": {
+ "html-escaper": "^2.0.0",
+ "istanbul-lib-report": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
}
},
- "istanbul-lib-report": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz",
- "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==",
+ "node_modules/jackspeak": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz",
+ "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==",
"dev": true,
- "requires": {
- "istanbul-lib-coverage": "^3.0.0",
- "make-dir": "^3.0.0",
- "supports-color": "^7.1.0"
- },
"dependencies": {
- "has-flag": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
- "dev": true
- },
- "supports-color": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
- "dev": true,
- "requires": {
- "has-flag": "^4.0.0"
- }
- }
+ "@isaacs/cliui": "^8.0.2"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ },
+ "optionalDependencies": {
+ "@pkgjs/parseargs": "^0.11.0"
}
},
- "istanbul-lib-source-maps": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz",
- "integrity": "sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg==",
+ "node_modules/js-yaml": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+ "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
"dev": true,
- "requires": {
- "debug": "^4.1.1",
- "istanbul-lib-coverage": "^3.0.0",
- "source-map": "^0.6.1"
- },
"dependencies": {
- "debug": {
- "version": "4.3.1",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
- "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
- "dev": true,
- "requires": {
- "ms": "2.1.2"
- }
- },
- "ms": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
- "dev": true
- },
- "source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
- "dev": true
- }
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
}
},
- "istanbul-reports": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.2.tgz",
- "integrity": "sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw==",
- "dev": true,
- "requires": {
- "html-escaper": "^2.0.0",
- "istanbul-lib-report": "^3.0.0"
- }
+ "node_modules/json-buffer": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
+ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
+ "dev": true
},
- "js-tokens": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
- "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true
+ },
+ "node_modules/json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
"dev": true
},
- "js-yaml": {
- "version": "3.13.1",
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
- "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
+ "node_modules/keyv": {
+ "version": "4.5.4",
+ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
+ "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
"dev": true,
- "requires": {
- "argparse": "^1.0.7",
- "esprima": "^4.0.0"
+ "dependencies": {
+ "json-buffer": "3.0.1"
}
},
- "jsesc": {
- "version": "2.5.2",
- "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
- "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
- "dev": true
+ "node_modules/levn": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+ "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+ "dev": true,
+ "dependencies": {
+ "prelude-ls": "^1.2.1",
+ "type-check": "~0.4.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
},
- "json5": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz",
- "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==",
+ "node_modules/linkify-it": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz",
+ "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==",
"dev": true,
- "requires": {
- "minimist": "^1.2.5"
+ "dependencies": {
+ "uc.micro": "^2.0.0"
}
},
- "locate-path": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
- "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
+ "node_modules/locate-path": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+ "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
"dev": true,
- "requires": {
- "p-locate": "^3.0.0",
- "path-exists": "^3.0.0"
+ "dependencies": {
+ "p-locate": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "lodash": {
- "version": "4.17.21",
- "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
- "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
+ "node_modules/lodash.get": {
+ "version": "4.4.2",
+ "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
+ "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==",
"dev": true
},
- "lodash.flattendeep": {
- "version": "4.4.0",
- "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz",
- "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=",
+ "node_modules/lodash.merge": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
"dev": true
},
- "log-symbols": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz",
- "integrity": "sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==",
+ "node_modules/lodash.truncate": {
+ "version": "4.4.2",
+ "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz",
+ "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==",
+ "dev": true
+ },
+ "node_modules/log-symbols": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz",
+ "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==",
"dev": true,
- "requires": {
- "chalk": "^2.4.2"
+ "dependencies": {
+ "chalk": "^4.1.0",
+ "is-unicode-supported": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "make-dir": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
- "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
+ "node_modules/lru-cache": {
+ "version": "10.4.3",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
+ "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
+ "dev": true
+ },
+ "node_modules/lunr": {
+ "version": "2.3.9",
+ "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz",
+ "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==",
+ "dev": true
+ },
+ "node_modules/make-dir": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz",
+ "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==",
"dev": true,
- "requires": {
- "semver": "^6.0.0"
- },
"dependencies": {
- "semver": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
- "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
- "dev": true
- }
+ "semver": "^7.5.3"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "make-error": {
- "version": "1.3.5",
- "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.5.tgz",
- "integrity": "sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g==",
- "dev": true
+ "node_modules/markdown-it": {
+ "version": "14.1.0",
+ "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz",
+ "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==",
+ "dev": true,
+ "dependencies": {
+ "argparse": "^2.0.1",
+ "entities": "^4.4.0",
+ "linkify-it": "^5.0.0",
+ "mdurl": "^2.0.0",
+ "punycode.js": "^2.3.1",
+ "uc.micro": "^2.1.0"
+ },
+ "bin": {
+ "markdown-it": "bin/markdown-it.mjs"
+ }
},
- "md5.js": {
+ "node_modules/md5.js": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
"integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==",
- "requires": {
+ "dev": true,
+ "dependencies": {
"hash-base": "^3.0.0",
"inherits": "^2.0.1",
"safe-buffer": "^5.1.2"
}
},
- "merge-descriptors": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
- "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=",
+ "node_modules/mdurl": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz",
+ "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==",
"dev": true
},
- "minimaldata": {
+ "node_modules/merge-descriptors": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz",
+ "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==",
+ "dev": true,
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/merge2": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
+ "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/micromatch": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
+ "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
+ "dev": true,
+ "dependencies": {
+ "braces": "^3.0.3",
+ "picomatch": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=8.6"
+ }
+ },
+ "node_modules/minimaldata": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/minimaldata/-/minimaldata-1.0.2.tgz",
- "integrity": "sha1-AfOywB2LJzmEP9hT1AY2xaLrdms=",
+ "integrity": "sha512-ZR9tWALR8ZszYd/zP34TmmVwRDVBNCT5+hkNXfTp3rpEDmZmgmYt1Sh/tu9qYFuPvSvEEEeJGE2BY8MBmeAzQQ==",
"dev": true,
- "requires": {
+ "dependencies": {
"bitcoin-ops": "^1.3.0",
"pushdata-bitcoin": "^1.0.1"
}
},
- "minimalistic-assert": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
- "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==",
- "dev": true
- },
- "minimalistic-crypto-utils": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz",
- "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=",
- "dev": true
- },
- "minimatch": {
- "version": "3.0.4",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
- "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+ "node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"dev": true,
- "requires": {
+ "dependencies": {
"brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
}
},
- "minimist": {
- "version": "1.2.5",
- "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
- "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
- "dev": true
- },
- "mkdirp": {
- "version": "0.5.3",
- "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.3.tgz",
- "integrity": "sha512-P+2gwrFqx8lhew375MQHHeTlY8AuOJSrGf0R5ddkEndUkmwpgUob/vQuBD1V22/Cw1/lJr4x+EjllSezBThzBg==",
+ "node_modules/minipass": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
+ "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
"dev": true,
- "requires": {
- "minimist": "^1.2.5"
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
}
},
- "mocha": {
- "version": "7.1.1",
- "resolved": "https://registry.npmjs.org/mocha/-/mocha-7.1.1.tgz",
- "integrity": "sha512-3qQsu3ijNS3GkWcccT5Zw0hf/rWvu1fTN9sPvEd81hlwsr30GX2GcDSSoBxo24IR8FelmrAydGC6/1J5QQP4WA==",
- "dev": true,
- "requires": {
- "ansi-colors": "3.2.3",
- "browser-stdout": "1.3.1",
- "chokidar": "3.3.0",
- "debug": "3.2.6",
- "diff": "3.5.0",
- "escape-string-regexp": "1.0.5",
- "find-up": "3.0.0",
- "glob": "7.1.3",
- "growl": "1.10.5",
- "he": "1.2.0",
- "js-yaml": "3.13.1",
- "log-symbols": "3.0.0",
- "minimatch": "3.0.4",
- "mkdirp": "0.5.3",
- "ms": "2.1.1",
- "node-environment-flags": "1.0.6",
- "object.assign": "4.1.0",
- "strip-json-comments": "2.0.1",
- "supports-color": "6.0.0",
- "which": "1.3.1",
- "wide-align": "1.1.3",
- "yargs": "13.3.2",
- "yargs-parser": "13.1.2",
- "yargs-unparser": "1.6.0"
- }
- },
- "module-not-found-error": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/module-not-found-error/-/module-not-found-error-1.0.1.tgz",
- "integrity": "sha1-z4tP9PKWQGdNbN0CsOO8UjwrvcA=",
- "dev": true
- },
- "ms": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
- "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==",
- "dev": true
+ "node_modules/mocha": {
+ "version": "10.7.3",
+ "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.7.3.tgz",
+ "integrity": "sha512-uQWxAu44wwiACGqjbPYmjo7Lg8sFrS3dQe7PP2FQI+woptP4vZXSMcfMyFL/e1yFEeEpV4RtyTpZROOKmxis+A==",
+ "dev": true,
+ "dependencies": {
+ "ansi-colors": "^4.1.3",
+ "browser-stdout": "^1.3.1",
+ "chokidar": "^3.5.3",
+ "debug": "^4.3.5",
+ "diff": "^5.2.0",
+ "escape-string-regexp": "^4.0.0",
+ "find-up": "^5.0.0",
+ "glob": "^8.1.0",
+ "he": "^1.2.0",
+ "js-yaml": "^4.1.0",
+ "log-symbols": "^4.1.0",
+ "minimatch": "^5.1.6",
+ "ms": "^2.1.3",
+ "serialize-javascript": "^6.0.2",
+ "strip-json-comments": "^3.1.1",
+ "supports-color": "^8.1.1",
+ "workerpool": "^6.5.1",
+ "yargs": "^16.2.0",
+ "yargs-parser": "^20.2.9",
+ "yargs-unparser": "^2.0.0"
+ },
+ "bin": {
+ "_mocha": "bin/_mocha",
+ "mocha": "bin/mocha.js"
+ },
+ "engines": {
+ "node": ">= 14.0.0"
+ }
},
- "nan": {
- "version": "2.14.2",
- "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz",
- "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==",
- "dev": true
+ "node_modules/mocha/node_modules/brace-expansion": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
},
- "node-environment-flags": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.6.tgz",
- "integrity": "sha512-5Evy2epuL+6TM0lCQGpFIj6KwiEsGh1SrHUhTbNX+sLbBtjidPZFAnVK9y5yU1+h//RitLbRHTIMyxQPtxMdHw==",
+ "node_modules/mocha/node_modules/cliui": {
+ "version": "7.0.4",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
+ "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
"dev": true,
- "requires": {
- "object.getownpropertydescriptors": "^2.0.3",
- "semver": "^5.7.0"
+ "dependencies": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.0",
+ "wrap-ansi": "^7.0.0"
}
},
- "node-preload": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz",
- "integrity": "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==",
+ "node_modules/mocha/node_modules/minimatch": {
+ "version": "5.1.6",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
+ "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
"dev": true,
- "requires": {
- "process-on-spawn": "^1.0.0"
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=10"
}
},
- "normalize-path": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
- "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "node_modules/mocha/node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"dev": true
},
- "npm-audit-whitelister": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/npm-audit-whitelister/-/npm-audit-whitelister-1.0.2.tgz",
- "integrity": "sha512-MNaYMUPI4P1cGcnLNvMv0XW4F5NkVEJv2aAfLqXXKY4cgo5lXCHl1h9eUIQnWLKM3WHVOqKzUipMzfunzQZXUg==",
- "dev": true
+ "node_modules/mocha/node_modules/supports-color": {
+ "version": "8.1.1",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
+ "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/supports-color?sponsor=1"
+ }
},
- "nyc": {
- "version": "15.1.0",
- "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.1.0.tgz",
- "integrity": "sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A==",
+ "node_modules/mocha/node_modules/yargs": {
+ "version": "16.2.0",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
+ "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
"dev": true,
- "requires": {
- "@istanbuljs/load-nyc-config": "^1.0.0",
- "@istanbuljs/schema": "^0.1.2",
- "caching-transform": "^4.0.0",
- "convert-source-map": "^1.7.0",
- "decamelize": "^1.2.0",
- "find-cache-dir": "^3.2.0",
- "find-up": "^4.1.0",
- "foreground-child": "^2.0.0",
- "get-package-type": "^0.1.0",
- "glob": "^7.1.6",
- "istanbul-lib-coverage": "^3.0.0",
- "istanbul-lib-hook": "^3.0.0",
- "istanbul-lib-instrument": "^4.0.0",
- "istanbul-lib-processinfo": "^2.0.2",
- "istanbul-lib-report": "^3.0.0",
- "istanbul-lib-source-maps": "^4.0.0",
- "istanbul-reports": "^3.0.2",
- "make-dir": "^3.0.0",
- "node-preload": "^0.2.1",
- "p-map": "^3.0.0",
- "process-on-spawn": "^1.0.0",
- "resolve-from": "^5.0.0",
- "rimraf": "^3.0.0",
- "signal-exit": "^3.0.2",
- "spawn-wrap": "^2.0.0",
- "test-exclude": "^6.0.0",
- "yargs": "^15.0.2"
- },
- "dependencies": {
- "ansi-regex": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
- "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
- "dev": true
- },
- "ansi-styles": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "dev": true,
- "requires": {
- "color-convert": "^2.0.1"
- }
- },
- "cliui": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
- "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
- "dev": true,
- "requires": {
- "string-width": "^4.2.0",
- "strip-ansi": "^6.0.0",
- "wrap-ansi": "^6.2.0"
- }
- },
- "color-convert": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "dev": true,
- "requires": {
- "color-name": "~1.1.4"
- }
- },
- "color-name": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
- "dev": true
- },
- "emoji-regex": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
- "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
- "dev": true
- },
- "find-up": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
- "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
- "dev": true,
- "requires": {
- "locate-path": "^5.0.0",
- "path-exists": "^4.0.0"
- }
- },
- "glob": {
- "version": "7.1.6",
- "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
- "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
- "dev": true,
- "requires": {
- "fs.realpath": "^1.0.0",
- "inflight": "^1.0.4",
- "inherits": "2",
- "minimatch": "^3.0.4",
- "once": "^1.3.0",
- "path-is-absolute": "^1.0.0"
- }
- },
- "is-fullwidth-code-point": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
- "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
- "dev": true
- },
- "locate-path": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
- "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
- "dev": true,
- "requires": {
- "p-locate": "^4.1.0"
- }
- },
- "p-locate": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
- "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
- "dev": true,
- "requires": {
- "p-limit": "^2.2.0"
- }
- },
- "path-exists": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
- "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
- "dev": true
- },
- "rimraf": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
- "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
- "dev": true,
- "requires": {
- "glob": "^7.1.3"
- }
- },
- "string-width": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz",
- "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==",
- "dev": true,
- "requires": {
- "emoji-regex": "^8.0.0",
- "is-fullwidth-code-point": "^3.0.0",
- "strip-ansi": "^6.0.0"
- }
- },
- "strip-ansi": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
- "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
- "dev": true,
- "requires": {
- "ansi-regex": "^5.0.0"
- }
- },
- "wrap-ansi": {
- "version": "6.2.0",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
- "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
- "dev": true,
- "requires": {
- "ansi-styles": "^4.0.0",
- "string-width": "^4.1.0",
- "strip-ansi": "^6.0.0"
- }
- },
- "yargs": {
- "version": "15.4.1",
- "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz",
- "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
- "dev": true,
- "requires": {
- "cliui": "^6.0.0",
- "decamelize": "^1.2.0",
- "find-up": "^4.1.0",
- "get-caller-file": "^2.0.1",
- "require-directory": "^2.1.1",
- "require-main-filename": "^2.0.0",
- "set-blocking": "^2.0.0",
- "string-width": "^4.2.0",
- "which-module": "^2.0.0",
- "y18n": "^4.0.0",
- "yargs-parser": "^18.1.2"
- }
- },
- "yargs-parser": {
- "version": "18.1.3",
- "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
- "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
- "dev": true,
- "requires": {
- "camelcase": "^5.0.0",
- "decamelize": "^1.2.0"
- }
- }
+ "dependencies": {
+ "cliui": "^7.0.2",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "require-directory": "^2.1.1",
+ "string-width": "^4.2.0",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^20.2.2"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/mocha/node_modules/yargs-parser": {
+ "version": "20.2.9",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
+ "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
}
},
- "object-inspect": {
- "version": "1.7.0",
- "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz",
- "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==",
+ "node_modules/module-not-found-error": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/module-not-found-error/-/module-not-found-error-1.0.1.tgz",
+ "integrity": "sha512-pEk4ECWQXV6z2zjhRZUongnLJNUeGQJ3w6OQ5ctGwD+i5o93qjRQUk2Rt6VdNeu3sEP0AB4LcfvdebpxBRVr4g==",
"dev": true
},
- "object-keys": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
- "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
+ "node_modules/ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
"dev": true
},
- "object.assign": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz",
- "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==",
- "dev": true,
- "requires": {
- "define-properties": "^1.1.2",
- "function-bind": "^1.1.1",
- "has-symbols": "^1.0.0",
- "object-keys": "^1.0.11"
- }
+ "node_modules/natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+ "dev": true
},
- "object.getownpropertydescriptors": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz",
- "integrity": "sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg==",
+ "node_modules/normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
"dev": true,
- "requires": {
- "define-properties": "^1.1.3",
- "es-abstract": "^1.17.0-next.1"
+ "engines": {
+ "node": ">=0.10.0"
}
},
- "once": {
+ "node_modules/once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
- "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
"dev": true,
- "requires": {
+ "dependencies": {
"wrappy": "1"
}
},
- "p-limit": {
- "version": "2.2.2",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz",
- "integrity": "sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==",
+ "node_modules/optionator": {
+ "version": "0.9.4",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
+ "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
"dev": true,
- "requires": {
- "p-try": "^2.0.0"
+ "dependencies": {
+ "deep-is": "^0.1.3",
+ "fast-levenshtein": "^2.0.6",
+ "levn": "^0.4.1",
+ "prelude-ls": "^1.2.1",
+ "type-check": "^0.4.0",
+ "word-wrap": "^1.2.5"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
}
},
- "p-locate": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
- "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
+ "node_modules/p-limit": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+ "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
"dev": true,
- "requires": {
- "p-limit": "^2.0.0"
+ "dependencies": {
+ "yocto-queue": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "p-map": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz",
- "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==",
+ "node_modules/p-locate": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+ "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
"dev": true,
- "requires": {
- "aggregate-error": "^3.0.0"
+ "dependencies": {
+ "p-limit": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "p-try": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
- "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
+ "node_modules/package-json-from-dist": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz",
+ "integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==",
"dev": true
},
- "package-hash": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz",
- "integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==",
+ "node_modules/parent-module": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
"dev": true,
- "requires": {
- "graceful-fs": "^4.1.15",
- "hasha": "^5.0.0",
- "lodash.flattendeep": "^4.4.0",
- "release-zalgo": "^1.0.0"
+ "dependencies": {
+ "callsites": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
}
},
- "path-exists": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
- "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
- "dev": true
+ "node_modules/path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
},
- "path-is-absolute": {
+ "node_modules/path-is-absolute": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
- "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
- "dev": true
+ "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
},
- "path-key": {
+ "node_modules/path-key": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
- "dev": true
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
},
- "path-parse": {
+ "node_modules/path-parse": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
"dev": true
},
- "pbkdf2": {
- "version": "3.0.17",
- "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz",
- "integrity": "sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==",
+ "node_modules/path-scurry": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
+ "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
"dev": true,
- "requires": {
- "create-hash": "^1.1.2",
- "create-hmac": "^1.1.4",
- "ripemd160": "^2.0.1",
- "safe-buffer": "^5.0.1",
- "sha.js": "^2.4.8"
+ "dependencies": {
+ "lru-cache": "^10.2.0",
+ "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
}
},
- "picomatch": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.1.tgz",
- "integrity": "sha512-ISBaA8xQNmwELC7eOjqFKMESB2VIqt4PPDD0nsS95b/9dZXvVKOlz9keMSnoGGKcOHXfTvDD6WMaRoSc9UuhRA==",
- "dev": true
- },
- "pkg-dir": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
- "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
+ "node_modules/picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
"dev": true,
- "requires": {
- "find-up": "^4.0.0"
+ "engines": {
+ "node": ">=8.6"
},
- "dependencies": {
- "find-up": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
- "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
- "dev": true,
- "requires": {
- "locate-path": "^5.0.0",
- "path-exists": "^4.0.0"
- }
- },
- "locate-path": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
- "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
- "dev": true,
- "requires": {
- "p-locate": "^4.1.0"
- }
- },
- "p-locate": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
- "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
- "dev": true,
- "requires": {
- "p-limit": "^2.2.0"
- }
- },
- "path-exists": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
- "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
- "dev": true
- }
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
}
},
- "prettier": {
- "version": "1.16.4",
- "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.16.4.tgz",
- "integrity": "sha512-ZzWuos7TI5CKUeQAtFd6Zhm2s6EpAD/ZLApIhsF9pRvRtM1RFo61dM/4MSRUA0SuLugA/zgrZD8m0BaY46Og7g==",
- "dev": true
+ "node_modules/prelude-ls": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8.0"
+ }
},
- "process-on-spawn": {
+ "node_modules/prettier": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz",
+ "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==",
+ "dev": true,
+ "bin": {
+ "prettier": "bin/prettier.cjs"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/prettier/prettier?sponsor=1"
+ }
+ },
+ "node_modules/prettier-linter-helpers": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz",
- "integrity": "sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg==",
+ "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz",
+ "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==",
"dev": true,
- "requires": {
- "fromentries": "^1.2.0"
+ "dependencies": {
+ "fast-diff": "^1.1.2"
+ },
+ "engines": {
+ "node": ">=6.0.0"
}
},
- "proxyquire": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/proxyquire/-/proxyquire-2.1.0.tgz",
- "integrity": "sha512-kptdFArCfGRtQFv3Qwjr10lwbEV0TBJYvfqzhwucyfEXqVgmnAkyEw/S3FYzR5HI9i5QOq4rcqQjZ6AlknlCDQ==",
+ "node_modules/proxyquire": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/proxyquire/-/proxyquire-2.1.3.tgz",
+ "integrity": "sha512-BQWfCqYM+QINd+yawJz23tbBM40VIGXOdDw3X344KcclI/gtBbdWF6SlQ4nK/bYhF9d27KYug9WzljHC6B9Ysg==",
"dev": true,
- "requires": {
+ "dependencies": {
"fill-keys": "^1.0.2",
- "module-not-found-error": "^1.0.0",
- "resolve": "~1.8.1"
+ "module-not-found-error": "^1.0.1",
+ "resolve": "^1.11.1"
+ }
+ },
+ "node_modules/punycode": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
+ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/punycode.js": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz",
+ "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
}
},
- "pushdata-bitcoin": {
+ "node_modules/pushdata-bitcoin": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/pushdata-bitcoin/-/pushdata-bitcoin-1.0.1.tgz",
- "integrity": "sha1-FZMdPNlnreUiBvUjqnMxrvfUOvc=",
+ "integrity": "sha512-hw7rcYTJRAl4olM8Owe8x0fBuJJ+WGbMhQuLWOXEMN3PxPCKQHRkhfL+XG0+iXUmSHjkMmb3Ba55Mt21cZc9kQ==",
"dev": true,
- "requires": {
+ "dependencies": {
"bitcoin-ops": "^1.3.0"
}
},
- "randombytes": {
+ "node_modules/queue-microtask": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
+ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
+ "node_modules/randombytes": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
"integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
"dev": true,
- "requires": {
+ "dependencies": {
"safe-buffer": "^5.1.0"
}
},
- "readdirp": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.2.0.tgz",
- "integrity": "sha512-crk4Qu3pmXwgxdSgGhgA/eXiJAPQiX4GMOZZMXnqKxHX7TaoL+3gQVo/WeuAiogr07DpnfjIMpXXa+PAIvwPGQ==",
+ "node_modules/readable-stream": {
+ "version": "3.6.2",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+ "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
"dev": true,
- "requires": {
- "picomatch": "^2.0.4"
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
}
},
- "regtest-client": {
+ "node_modules/readdirp": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+ "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+ "dev": true,
+ "dependencies": {
+ "picomatch": "^2.2.1"
+ },
+ "engines": {
+ "node": ">=8.10.0"
+ }
+ },
+ "node_modules/regtest-client": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/regtest-client/-/regtest-client-0.2.0.tgz",
"integrity": "sha512-eIcC8Kle/wjS47pRlw7nJpstrJDWp0bkvVPl2KJpJcK3JDNW0fMxJgE/CGpMEUSjhhFXW1rtJMN6kyKw5NIzqg==",
"dev": true,
- "requires": {
+ "dependencies": {
"bs58check": "^2.1.2",
"dhttp": "^3.0.3",
"randombytes": "^2.1.0"
}
},
- "release-zalgo": {
+ "node_modules/regtest-client/node_modules/bs58": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz",
+ "integrity": "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==",
+ "dev": true,
+ "dependencies": {
+ "base-x": "^3.0.2"
+ }
+ },
+ "node_modules/regtest-client/node_modules/bs58check": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/bs58check/-/bs58check-2.1.2.tgz",
+ "integrity": "sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA==",
+ "dev": true,
+ "dependencies": {
+ "bs58": "^4.0.0",
+ "create-hash": "^1.1.0",
+ "safe-buffer": "^5.1.2"
+ }
+ },
+ "node_modules/require-directory": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+ "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/require-from-string": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
+ "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/resolve": {
+ "version": "1.22.8",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz",
+ "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==",
+ "dev": true,
+ "dependencies": {
+ "is-core-module": "^2.13.0",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ },
+ "bin": {
+ "resolve": "bin/resolve"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/resolve-from": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/resolve-pkg-maps": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz",
- "integrity": "sha1-CXALflB0Mpc5Mw5TXFqQ+2eFFzA=",
+ "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
+ "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
"dev": true,
- "requires": {
- "es6-error": "^4.0.1"
+ "funding": {
+ "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
}
},
- "require-directory": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
- "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
- "dev": true
- },
- "require-main-filename": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
- "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
- "dev": true
- },
- "resolve": {
- "version": "1.8.1",
- "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.8.1.tgz",
- "integrity": "sha512-AicPrAC7Qu1JxPCZ9ZgCZlY35QgFnNqc+0LtbRNxnVw4TXvjQ72wnuL9JQcEBgXkI9JM8MsT9kaQoHcpCRJOYA==",
+ "node_modules/reusify": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
+ "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
"dev": true,
- "requires": {
- "path-parse": "^1.0.5"
+ "engines": {
+ "iojs": ">=1.0.0",
+ "node": ">=0.10.0"
}
},
- "resolve-from": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
- "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
- "dev": true
- },
- "rimraf": {
- "version": "2.6.3",
- "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz",
- "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
+ "node_modules/rimraf": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+ "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+ "deprecated": "Rimraf versions prior to v4 are no longer supported",
"dev": true,
- "requires": {
+ "dependencies": {
"glob": "^7.1.3"
},
+ "bin": {
+ "rimraf": "bin.js"
+ }
+ },
+ "node_modules/rimraf/node_modules/glob": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "deprecated": "Glob versions prior to v9 are no longer supported",
+ "dev": true,
"dependencies": {
- "glob": {
- "version": "7.1.4",
- "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz",
- "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==",
- "dev": true,
- "requires": {
- "fs.realpath": "^1.0.0",
- "inflight": "^1.0.4",
- "inherits": "2",
- "minimatch": "^3.0.4",
- "once": "^1.3.0",
- "path-is-absolute": "^1.0.0"
- }
- }
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.1.1",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
}
},
- "ripemd160": {
+ "node_modules/ripemd160": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz",
"integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==",
- "requires": {
+ "dev": true,
+ "dependencies": {
"hash-base": "^3.0.0",
"inherits": "^2.0.1"
}
},
- "safe-buffer": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
- "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+ "node_modules/run-parallel": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
+ "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "dependencies": {
+ "queue-microtask": "^1.2.2"
+ }
},
- "semver": {
- "version": "5.7.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
- "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
- "dev": true
+ "node_modules/safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
},
- "set-blocking": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
- "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
- "dev": true
+ "node_modules/semver": {
+ "version": "7.6.3",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
+ "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/serialize-javascript": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz",
+ "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==",
+ "dev": true,
+ "dependencies": {
+ "randombytes": "^2.1.0"
+ }
},
- "sha.js": {
+ "node_modules/sha.js": {
"version": "2.4.11",
"resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz",
"integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==",
- "requires": {
+ "dev": true,
+ "dependencies": {
"inherits": "^2.0.1",
"safe-buffer": "^5.0.1"
+ },
+ "bin": {
+ "sha.js": "bin.js"
}
},
- "shebang-command": {
+ "node_modules/shebang-command": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
"dev": true,
- "requires": {
+ "dependencies": {
"shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
}
},
- "shebang-regex": {
+ "node_modules/shebang-regex": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
- "dev": true
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
},
- "signal-exit": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz",
- "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==",
- "dev": true
+ "node_modules/shiki": {
+ "version": "1.16.1",
+ "resolved": "https://registry.npmjs.org/shiki/-/shiki-1.16.1.tgz",
+ "integrity": "sha512-tCJIMaxDVB1mEIJ5TvfZU7kCPB5eo9fli5+21Olc/bmyv+w8kye3JOp+LZRmGkAyT71hrkefQhTiY+o9mBikRQ==",
+ "dev": true,
+ "dependencies": {
+ "@shikijs/core": "1.16.1",
+ "@shikijs/vscode-textmate": "^9.2.0",
+ "@types/hast": "^3.0.4"
+ }
},
- "source-map": {
- "version": "0.5.7",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
- "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
- "dev": true
+ "node_modules/signal-exit": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
+ "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
+ "dev": true,
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
},
- "source-map-support": {
- "version": "0.5.13",
- "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz",
- "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==",
+ "node_modules/slice-ansi": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz",
+ "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==",
"dev": true,
- "requires": {
- "buffer-from": "^1.0.0",
- "source-map": "^0.6.0"
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "astral-regex": "^2.0.0",
+ "is-fullwidth-code-point": "^3.0.0"
},
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/slice-ansi?sponsor=1"
+ }
+ },
+ "node_modules/statuses": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
+ "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/string_decoder": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
+ "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+ "dev": true,
"dependencies": {
- "source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
- "dev": true
- }
+ "safe-buffer": "~5.2.0"
}
},
- "spawn-wrap": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz",
- "integrity": "sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==",
- "dev": true,
- "requires": {
- "foreground-child": "^2.0.0",
- "is-windows": "^1.0.2",
- "make-dir": "^3.0.0",
- "rimraf": "^3.0.0",
- "signal-exit": "^3.0.2",
- "which": "^2.0.1"
+ "node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
},
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/string-width-cjs": {
+ "name": "string-width",
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
"dependencies": {
- "rimraf": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
- "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
- "dev": true,
- "requires": {
- "glob": "^7.1.3"
- }
- },
- "which": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
- "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
- "dev": true,
- "requires": {
- "isexe": "^2.0.0"
- }
- }
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
}
},
- "sprintf-js": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
- "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
- "dev": true
+ "node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
},
- "statuses": {
- "version": "1.5.0",
- "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
- "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=",
- "dev": true
+ "node_modules/strip-ansi-cjs": {
+ "name": "strip-ansi",
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
},
- "string-width": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
- "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
+ "node_modules/strip-json-comments": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
"dev": true,
- "requires": {
- "is-fullwidth-code-point": "^2.0.0",
- "strip-ansi": "^4.0.0"
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "string.prototype.trimleft": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz",
- "integrity": "sha512-iu2AGd3PuP5Rp7x2kEZCrB2Nf41ehzh+goo8TV7z8/XDBbsvc6HQIlUl9RjkZ4oyrW1XM5UwlGl1oVEaDjg6Ag==",
+ "node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dev": true,
- "requires": {
- "define-properties": "^1.1.3",
- "function-bind": "^1.1.1"
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
}
},
- "string.prototype.trimright": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.1.tgz",
- "integrity": "sha512-qFvWL3/+QIgZXVmJBfpHmxLB7xsUXz6HsUmP8+5dRaC3Q7oKUv9Vo6aMCRZC1smrtyECFsIT30PqBJ1gTjAs+g==",
+ "node_modules/supports-preserve-symlinks-flag": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
+ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
"dev": true,
- "requires": {
- "define-properties": "^1.1.3",
- "function-bind": "^1.1.1"
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
}
},
- "strip-ansi": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
- "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+ "node_modules/synckit": {
+ "version": "0.9.1",
+ "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.1.tgz",
+ "integrity": "sha512-7gr8p9TQP6RAHusBOSLs46F4564ZrjV8xFmw5zCmgmhGUcw2hxsShhJ6CEiHQMgPDwAQ1fWHPM0ypc4RMAig4A==",
+ "dev": true,
+ "dependencies": {
+ "@pkgr/core": "^0.1.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": "^14.18.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/unts"
+ }
+ },
+ "node_modules/table": {
+ "version": "6.8.2",
+ "resolved": "https://registry.npmjs.org/table/-/table-6.8.2.tgz",
+ "integrity": "sha512-w2sfv80nrAh2VCbqR5AK27wswXhqcck2AhfnNW76beQXskGZ1V12GwS//yYVa3d3fcvAip2OUnbDAjW2k3v9fA==",
"dev": true,
- "requires": {
- "ansi-regex": "^3.0.0"
+ "dependencies": {
+ "ajv": "^8.0.1",
+ "lodash.truncate": "^4.4.2",
+ "slice-ansi": "^4.0.0",
+ "string-width": "^4.2.3",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=10.0.0"
}
},
- "strip-bom": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz",
- "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==",
- "dev": true
+ "node_modules/table/node_modules/ajv": {
+ "version": "8.17.1",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
+ "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
+ "dev": true,
+ "dependencies": {
+ "fast-deep-equal": "^3.1.3",
+ "fast-uri": "^3.0.1",
+ "json-schema-traverse": "^1.0.0",
+ "require-from-string": "^2.0.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
},
- "strip-json-comments": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
- "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=",
+ "node_modules/table/node_modules/json-schema-traverse": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
"dev": true
},
- "supports-color": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz",
- "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==",
+ "node_modules/test-exclude": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-7.0.1.tgz",
+ "integrity": "sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==",
"dev": true,
- "requires": {
- "has-flag": "^3.0.0"
+ "dependencies": {
+ "@istanbuljs/schema": "^0.1.2",
+ "glob": "^10.4.1",
+ "minimatch": "^9.0.4"
+ },
+ "engines": {
+ "node": ">=18"
}
},
- "test-exclude": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz",
- "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==",
+ "node_modules/test-exclude/node_modules/brace-expansion": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
"dev": true,
- "requires": {
- "@istanbuljs/schema": "^0.1.2",
- "glob": "^7.1.4",
- "minimatch": "^3.0.4"
- },
- "dependencies": {
- "glob": {
- "version": "7.1.6",
- "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
- "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
- "dev": true,
- "requires": {
- "fs.realpath": "^1.0.0",
- "inflight": "^1.0.4",
- "inherits": "2",
- "minimatch": "^3.0.4",
- "once": "^1.3.0",
- "path-is-absolute": "^1.0.0"
- }
- }
+ "dependencies": {
+ "balanced-match": "^1.0.0"
}
},
- "tiny-secp256k1": {
- "version": "1.1.6",
- "resolved": "https://registry.npmjs.org/tiny-secp256k1/-/tiny-secp256k1-1.1.6.tgz",
- "integrity": "sha512-FmqJZGduTyvsr2cF3375fqGHUovSwDi/QytexX1Se4BPuPZpTE5Ftp5fg+EFSuEf3lhZqgCRjEG3ydUQ/aNiwA==",
+ "node_modules/test-exclude/node_modules/glob": {
+ "version": "10.4.5",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
+ "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==",
"dev": true,
- "requires": {
- "bindings": "^1.3.0",
- "bn.js": "^4.11.8",
- "create-hmac": "^1.1.7",
- "elliptic": "^6.4.0",
- "nan": "^2.13.2"
+ "dependencies": {
+ "foreground-child": "^3.1.0",
+ "jackspeak": "^3.1.2",
+ "minimatch": "^9.0.4",
+ "minipass": "^7.1.2",
+ "package-json-from-dist": "^1.0.0",
+ "path-scurry": "^1.11.1"
+ },
+ "bin": {
+ "glob": "dist/esm/bin.mjs"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
}
},
- "to-fast-properties": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
- "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=",
+ "node_modules/test-exclude/node_modules/minimatch": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+ "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/text-table": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
+ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
"dev": true
},
- "to-regex-range": {
+ "node_modules/tiny-secp256k1": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/tiny-secp256k1/-/tiny-secp256k1-2.2.3.tgz",
+ "integrity": "sha512-SGcL07SxcPN2nGKHTCvRMkQLYPSoeFcvArUSCYtjVARiFAWU44cCIqYS0mYAU6nY7XfvwURuTIGo2Omt3ZQr0Q==",
+ "dev": true,
+ "dependencies": {
+ "uint8array-tools": "0.0.7"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/tiny-secp256k1/node_modules/uint8array-tools": {
+ "version": "0.0.7",
+ "resolved": "https://registry.npmjs.org/uint8array-tools/-/uint8array-tools-0.0.7.tgz",
+ "integrity": "sha512-vrrNZJiusLWoFWBqz5Y5KMCgP9W9hnjZHzZiZRT8oNAkq3d5Z5Oe76jAvVVSRh4U8GGR90N2X1dWtrhvx6L8UQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
"dev": true,
- "requires": {
+ "dependencies": {
"is-number": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8.0"
}
},
- "ts-node": {
- "version": "8.3.0",
- "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.3.0.tgz",
- "integrity": "sha512-dyNS/RqyVTDcmNM4NIBAeDMpsAdaQ+ojdf0GOLqE6nwJOgzEkdRNzJywhDfwnuvB10oa6NLVG1rUJQCpRN7qoQ==",
- "dev": true,
- "requires": {
- "arg": "^4.1.0",
- "diff": "^4.0.1",
- "make-error": "^1.1.1",
- "source-map-support": "^0.5.6",
- "yn": "^3.0.0"
- },
- "dependencies": {
- "diff": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.1.tgz",
- "integrity": "sha512-s2+XdvhPCOF01LRQBC8hf4vhbVmI2CGS5aZnxLJlT5FtdhPCDFq80q++zK2KlrVorVDdL5BOGZ/VfLrVtYNF+Q==",
- "dev": true
- }
+ "node_modules/ts-api-utils": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz",
+ "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=16"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.2.0"
}
},
- "tslib": {
- "version": "1.14.1",
- "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
- "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
+ "node_modules/tslib": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz",
+ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==",
"dev": true
},
- "tslint": {
- "version": "6.1.3",
- "resolved": "https://registry.npmjs.org/tslint/-/tslint-6.1.3.tgz",
- "integrity": "sha512-IbR4nkT96EQOvKE2PW/djGz8iGNeJ4rF2mBfiYaR/nvUWYKJhLwimoJKgjIFEIDibBtOevj7BqCRL4oHeWWUCg==",
- "dev": true,
- "requires": {
- "@babel/code-frame": "^7.0.0",
- "builtin-modules": "^1.1.1",
- "chalk": "^2.3.0",
- "commander": "^2.12.1",
- "diff": "^4.0.1",
- "glob": "^7.1.1",
- "js-yaml": "^3.13.1",
- "minimatch": "^3.0.4",
- "mkdirp": "^0.5.3",
- "resolve": "^1.3.2",
- "semver": "^5.3.0",
- "tslib": "^1.13.0",
- "tsutils": "^2.29.0"
- },
- "dependencies": {
- "diff": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
- "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
- "dev": true
- }
+ "node_modules/tsx": {
+ "version": "4.19.3",
+ "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.3.tgz",
+ "integrity": "sha512-4H8vUNGNjQ4V2EOoGw005+c+dGuPSnhpPBPHBtsZdGZBk/iJb4kguGlPWaZTZ3q5nMtFOEsY0nRDlh9PJyd6SQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "esbuild": "~0.25.0",
+ "get-tsconfig": "^4.7.5"
+ },
+ "bin": {
+ "tsx": "dist/cli.mjs"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
}
},
- "tsutils": {
- "version": "2.29.0",
- "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz",
- "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==",
+ "node_modules/type-check": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+ "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
"dev": true,
- "requires": {
- "tslib": "^1.8.1"
+ "dependencies": {
+ "prelude-ls": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
}
},
- "type-fest": {
- "version": "0.8.1",
- "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz",
- "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==",
- "dev": true
+ "node_modules/typedoc": {
+ "version": "0.26.6",
+ "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.26.6.tgz",
+ "integrity": "sha512-SfEU3SH3wHNaxhFPjaZE2kNl/NFtLNW5c1oHsg7mti7GjmUj1Roq6osBQeMd+F4kL0BoRBBr8gQAuqBlfFu8LA==",
+ "dev": true,
+ "dependencies": {
+ "lunr": "^2.3.9",
+ "markdown-it": "^14.1.0",
+ "minimatch": "^9.0.5",
+ "shiki": "^1.9.1",
+ "yaml": "^2.4.5"
+ },
+ "bin": {
+ "typedoc": "bin/typedoc"
+ },
+ "engines": {
+ "node": ">= 18"
+ },
+ "peerDependencies": {
+ "typescript": "4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x"
+ }
},
- "typedarray-to-buffer": {
- "version": "3.1.5",
- "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz",
- "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==",
+ "node_modules/typedoc/node_modules/brace-expansion": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
"dev": true,
- "requires": {
- "is-typedarray": "^1.0.0"
+ "dependencies": {
+ "balanced-match": "^1.0.0"
}
},
- "typeforce": {
- "version": "1.18.0",
- "resolved": "https://registry.npmjs.org/typeforce/-/typeforce-1.18.0.tgz",
- "integrity": "sha512-7uc1O8h1M1g0rArakJdf0uLRSSgFcYexrVoKo+bzJd32gd4gDy2L/Z+8/FjPnU9ydY3pEnVPtr9FyscYY60K1g=="
+ "node_modules/typedoc/node_modules/minimatch": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+ "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/typescript": {
+ "version": "5.5.4",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz",
+ "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==",
+ "devOptional": true,
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
},
- "typescript": {
- "version": "4.4.4",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.4.tgz",
- "integrity": "sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA==",
+ "node_modules/uc.micro": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz",
+ "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==",
"dev": true
},
- "uuid": {
- "version": "3.4.0",
- "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
- "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
+ "node_modules/uint8array-tools": {
+ "version": "0.0.9",
+ "resolved": "https://registry.npmjs.org/uint8array-tools/-/uint8array-tools-0.0.9.tgz",
+ "integrity": "sha512-9vqDWmoSXOoi+K14zNaf6LBV51Q8MayF0/IiQs3GlygIKUYtog603e6virExkjjFosfJUBI4LhbQK1iq8IG11A==",
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "5.26.5",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
+ "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
"dev": true
},
- "varuint-bitcoin": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/varuint-bitcoin/-/varuint-bitcoin-1.1.2.tgz",
- "integrity": "sha512-4EVb+w4rx+YfVM32HQX42AbbT7/1f5zwAYhIujKXKk8NQK+JfRVl3pqT3hjNn/L+RstigmGGKVwHA/P0wgITZw==",
- "requires": {
- "safe-buffer": "^5.1.1"
+ "node_modules/uri-js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dev": true,
+ "dependencies": {
+ "punycode": "^2.1.0"
}
},
- "which": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
- "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
+ "node_modules/util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
+ "dev": true
+ },
+ "node_modules/v8-to-istanbul": {
+ "version": "9.3.0",
+ "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz",
+ "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==",
"dev": true,
- "requires": {
- "isexe": "^2.0.0"
+ "dependencies": {
+ "@jridgewell/trace-mapping": "^0.3.12",
+ "@types/istanbul-lib-coverage": "^2.0.1",
+ "convert-source-map": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10.12.0"
+ }
+ },
+ "node_modules/valibot": {
+ "version": "0.38.0",
+ "resolved": "https://registry.npmjs.org/valibot/-/valibot-0.38.0.tgz",
+ "integrity": "sha512-RCJa0fetnzp+h+KN9BdgYOgtsMAG9bfoJ9JSjIhFHobKWVWyzM3jjaeNTdpFK9tQtf3q1sguXeERJ/LcmdFE7w==",
+ "peerDependencies": {
+ "typescript": ">=5"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
}
},
- "which-module": {
+ "node_modules/varuint-bitcoin": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
- "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=",
- "dev": true
+ "resolved": "https://registry.npmjs.org/varuint-bitcoin/-/varuint-bitcoin-2.0.0.tgz",
+ "integrity": "sha512-6QZbU/rHO2ZQYpWFDALCDSRsXbAs1VOEmXAxtbtjLtKuMJ/FQ8YbhfxlaiKv5nklci0M6lZtlZyxo9Q+qNnyog==",
+ "dependencies": {
+ "uint8array-tools": "^0.0.8"
+ }
+ },
+ "node_modules/varuint-bitcoin/node_modules/uint8array-tools": {
+ "version": "0.0.8",
+ "resolved": "https://registry.npmjs.org/uint8array-tools/-/uint8array-tools-0.0.8.tgz",
+ "integrity": "sha512-xS6+s8e0Xbx++5/0L+yyexukU7pz//Yg6IHg3BKhXotg1JcYtgxVcUctQ0HxLByiJzpAkNFawz1Nz5Xadzo82g==",
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
},
- "wide-align": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
- "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==",
+ "node_modules/wif": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/wif/-/wif-5.0.0.tgz",
+ "integrity": "sha512-iFzrC/9ne740qFbNjTZ2FciSRJlHIXoxqk/Y5EnE08QOXu1WjJyCCswwDTYbohAOEnlCtLaAAQBhyaLRFh2hMA==",
"dev": true,
- "requires": {
- "string-width": "^1.0.2 || 2"
+ "dependencies": {
+ "bs58check": "^4.0.0"
}
},
- "wif": {
- "version": "2.0.6",
- "resolved": "https://registry.npmjs.org/wif/-/wif-2.0.6.tgz",
- "integrity": "sha1-CNP1IFbGZnkplyb63g1DKudLRwQ=",
- "requires": {
- "bs58check": "<3.0.0"
+ "node_modules/word-wrap": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
+ "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
}
},
- "wrap-ansi": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz",
- "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==",
+ "node_modules/workerpool": {
+ "version": "6.5.1",
+ "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz",
+ "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==",
+ "dev": true
+ },
+ "node_modules/wrap-ansi": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
"dev": true,
- "requires": {
- "ansi-styles": "^3.2.0",
- "string-width": "^3.0.0",
- "strip-ansi": "^5.0.0"
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
},
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi-cjs": {
+ "name": "wrap-ansi",
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "dev": true,
"dependencies": {
- "ansi-regex": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
- "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
- "dev": true
- },
- "string-width": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
- "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
- "dev": true,
- "requires": {
- "emoji-regex": "^7.0.1",
- "is-fullwidth-code-point": "^2.0.0",
- "strip-ansi": "^5.1.0"
- }
- },
- "strip-ansi": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
- "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
- "dev": true,
- "requires": {
- "ansi-regex": "^4.1.0"
- }
- }
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
}
},
- "wrappy": {
+ "node_modules/wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
- "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
"dev": true
},
- "write-file-atomic": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz",
- "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==",
+ "node_modules/y18n": {
+ "version": "5.0.8",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
"dev": true,
- "requires": {
- "imurmurhash": "^0.1.4",
- "is-typedarray": "^1.0.0",
- "signal-exit": "^3.0.2",
- "typedarray-to-buffer": "^3.1.5"
+ "engines": {
+ "node": ">=10"
}
},
- "y18n": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
- "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==",
- "dev": true
+ "node_modules/yaml": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.5.1.tgz",
+ "integrity": "sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==",
+ "dev": true,
+ "bin": {
+ "yaml": "bin.mjs"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
},
- "yargs": {
- "version": "13.3.2",
- "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz",
- "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==",
+ "node_modules/yargs": {
+ "version": "17.7.2",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
+ "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
"dev": true,
- "requires": {
- "cliui": "^5.0.0",
- "find-up": "^3.0.0",
- "get-caller-file": "^2.0.1",
+ "dependencies": {
+ "cliui": "^8.0.1",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
"require-directory": "^2.1.1",
- "require-main-filename": "^2.0.0",
- "set-blocking": "^2.0.0",
- "string-width": "^3.0.0",
- "which-module": "^2.0.0",
- "y18n": "^4.0.0",
- "yargs-parser": "^13.1.2"
- },
- "dependencies": {
- "ansi-regex": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
- "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
- "dev": true
- },
- "string-width": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
- "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
- "dev": true,
- "requires": {
- "emoji-regex": "^7.0.1",
- "is-fullwidth-code-point": "^2.0.0",
- "strip-ansi": "^5.1.0"
- }
- },
- "strip-ansi": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
- "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
- "dev": true,
- "requires": {
- "ansi-regex": "^4.1.0"
- }
- }
+ "string-width": "^4.2.3",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^21.1.1"
+ },
+ "engines": {
+ "node": ">=12"
}
},
- "yargs-parser": {
- "version": "13.1.2",
- "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz",
- "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==",
+ "node_modules/yargs-parser": {
+ "version": "21.1.1",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
+ "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
"dev": true,
- "requires": {
- "camelcase": "^5.0.0",
- "decamelize": "^1.2.0"
+ "engines": {
+ "node": ">=12"
}
},
- "yargs-unparser": {
- "version": "1.6.0",
- "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.0.tgz",
- "integrity": "sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw==",
+ "node_modules/yargs-unparser": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz",
+ "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==",
"dev": true,
- "requires": {
- "flat": "^4.1.0",
- "lodash": "^4.17.15",
- "yargs": "^13.3.0"
+ "dependencies": {
+ "camelcase": "^6.0.0",
+ "decamelize": "^4.0.0",
+ "flat": "^5.0.2",
+ "is-plain-obj": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=10"
}
},
- "yn": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
- "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
- "dev": true
+ "node_modules/yocto-queue": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
}
}
}
diff --git a/package.json b/package.json
index e420511c8..9a87a0d9e 100644
--- a/package.json
+++ b/package.json
@@ -1,11 +1,25 @@
{
- "name": "bitcoinjs-lib",
- "version": "6.0.0",
+ "name": "@onekeyfe/bitcoinjs-lib",
+ "version": "7.0.1",
"description": "Client-side Bitcoin JavaScript library",
- "main": "./src/index.js",
- "types": "./src/index.d.ts",
+ "type": "module",
+ "main": "./src/cjs/index.cjs",
+ "module": "./src/esm/index.js",
+ "types": "./src/cjs/index.d.ts",
+ "exports": {
+ ".": {
+ "require": "./src/cjs/index.cjs",
+ "import": "./src/esm/index.js",
+ "types": "./src/cjs/index.d.ts"
+ },
+ "./src/*": {
+ "require": "./src/cjs/*.cjs",
+ "types": "./src/cjs/*.d.ts",
+ "import": "./src/esm/*.js"
+ }
+ },
"engines": {
- "node": ">=8.0.0"
+ "node": ">=18.0.0"
},
"keywords": [
"bitcoinjs",
@@ -15,25 +29,25 @@
"bitcoinjs"
],
"scripts": {
- "audit": "NPM_AUDIT_IGNORE_DEV=1 NPM_AUDIT_IGNORE_LEVEL=low npm-audit-whitelister .npm-audit-whitelister.json",
- "build": "npm run clean && tsc -p ./tsconfig.json && npm run formatjs",
- "build:tests": "npm run clean:jstests && tsc -p ./test/tsconfig.json",
+ "audit": "better-npm-audit audit -l high",
+ "build": "npm run clean && tsc -p ./tsconfig.json && tsc -p ./tsconfig.cjs.json && npm run formatjs",
+ "postbuild": "find src/cjs -type f -name \"*.js\" -exec bash -c 'mv \"$0\" \"${0%.js}.cjs\"' {} \\; && chmod +x ./fixup.cjs && node fixup.cjs",
"clean": "rimraf src",
- "clean:jstests": "rimraf 'test/**/!(ts-node-register)*.js'",
"coverage-report": "npm run build && npm run nobuild:coverage-report",
"coverage-html": "npm run build && npm run nobuild:coverage-html",
"coverage": "npm run build && npm run nobuild:coverage",
+ "doc": "typedoc",
"format": "npm run prettier -- --write",
"formatjs": "npm run prettierjs -- --write",
"format:ci": "npm run prettier -- --check && npm run prettierjs -- --check",
"gitdiff:ci": "npm run build && git diff --exit-code",
"integration": "npm run build && npm run nobuild:integration",
- "lint": "tslint -p tsconfig.json -c tslint.json",
- "lint:tests": "tslint -p test/tsconfig.json -c tslint.json",
- "mocha:ts": "mocha --recursive --require test/ts-node-register",
- "nobuild:coverage-report": "nyc report --reporter=lcov",
- "nobuild:coverage-html": "nyc report --reporter=html",
- "nobuild:coverage": "npm run build:tests && nyc --check-coverage --branches 90 --functions 90 --lines 90 mocha && npm run clean:jstests",
+ "lint": "eslint ts_src/** src/**/*.js",
+ "lint:tests": "eslint test/**/*.spec.ts",
+ "mocha:ts": "mocha --recursive",
+ "nobuild:coverage-report": "c8 report --reporter=lcov",
+ "nobuild:coverage-html": "c8 report --reporter=html",
+ "nobuild:coverage": "c8 --check-coverage --branches 85 --functions 90 --lines 90 npm run nobuild:unit",
"nobuild:integration": "npm run mocha:ts -- --timeout 50000 'test/integration/*.ts'",
"nobuild:unit": "npm run mocha:ts -- 'test/*.ts'",
"prettier": "prettier \"ts_src/**/*.ts\" \"test/**/*.ts\" --ignore-path ./.prettierignore",
@@ -49,44 +63,51 @@
"src"
],
"dependencies": {
+ "@noble/hashes": "^1.2.0",
"bech32": "^2.0.0",
- "bip174": "^2.0.1",
- "bs58check": "^2.1.2",
- "create-hash": "^1.1.0",
- "typeforce": "^1.11.3",
- "varuint-bitcoin": "^1.1.2",
- "wif": "^2.0.1"
+ "bip174": "^3.0.0",
+ "bs58check": "^4.0.0",
+ "uint8array-tools": "^0.0.9",
+ "valibot": "^0.38.0",
+ "varuint-bitcoin": "^2.0.0"
},
"devDependencies": {
+ "bitcoinjs-lib": ".",
+ "@eslint/eslintrc": "^3.1.0",
+ "@eslint/js": "^9.9.1",
"@types/bs58": "^4.0.0",
"@types/bs58check": "^2.1.0",
- "@types/create-hash": "^1.2.2",
"@types/mocha": "^5.2.7",
- "@types/node": "^16.11.7",
+ "@types/node": "^18.7.14",
"@types/proxyquire": "^1.3.28",
"@types/randombytes": "^2.0.0",
- "@types/wif": "^2.0.2",
- "bip32": "^2.0.6",
- "bip39": "^3.0.2",
+ "@typescript-eslint/eslint-plugin": "^8.2.0",
+ "@typescript-eslint/parser": "^8.2.0",
+ "better-npm-audit": "^3.7.3",
+ "bip32": "^5.0.0-rc.0",
+ "bip39": "^3.1.0",
"bip65": "^1.0.1",
"bip68": "^1.0.3",
- "bn.js": "^4.11.8",
- "bs58": "^4.0.0",
+ "bs58": "^6.0.0",
"dhttp": "^3.0.0",
- "ecpair": "^1.0.0",
+ "ecpair": "^3.0.0-rc.0",
+ "eslint": "^9.9.0",
+ "eslint-config-prettier": "^9.1.0",
+ "eslint-plugin-prettier": "^5.2.1",
+ "globals": "^15.9.0",
"hoodwink": "^2.0.0",
"minimaldata": "^1.0.2",
- "mocha": "^7.1.1",
- "npm-audit-whitelister": "^1.0.2",
- "nyc": "^15.1.0",
- "prettier": "1.16.4",
+ "mocha": "^10.6.0",
+ "c8": "^10.1.2",
+ "prettier": "^3.0.0",
"proxyquire": "^2.0.1",
"randombytes": "^2.1.0",
"regtest-client": "0.2.0",
"rimraf": "^2.6.3",
- "ts-node": "^8.3.0",
- "tslint": "^6.1.3",
- "typescript": "^4.4.4"
+ "tiny-secp256k1": "^2.2.0",
+ "tsx": "^4.17.0",
+ "typedoc": "^0.26.6",
+ "typescript": "^5.0.4"
},
"license": "MIT"
}
diff --git a/src/address.d.ts b/src/address.d.ts
deleted file mode 100644
index be0e00a61..000000000
--- a/src/address.d.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-///
-import { Network } from './networks';
-export interface Base58CheckResult {
- hash: Buffer;
- version: number;
-}
-export interface Bech32Result {
- version: number;
- prefix: string;
- data: Buffer;
-}
-export declare function fromBase58Check(address: string): Base58CheckResult;
-export declare function fromBech32(address: string): Bech32Result;
-export declare function toBase58Check(hash: Buffer, version: number): string;
-export declare function toBech32(data: Buffer, version: number, prefix: string): string;
-export declare function fromOutputScript(output: Buffer, network?: Network): string;
-export declare function toOutputScript(address: string, network?: Network): Buffer;
diff --git a/src/address.js b/src/address.js
deleted file mode 100644
index 12938fc67..000000000
--- a/src/address.js
+++ /dev/null
@@ -1,140 +0,0 @@
-'use strict';
-Object.defineProperty(exports, '__esModule', { value: true });
-exports.toOutputScript = exports.fromOutputScript = exports.toBech32 = exports.toBase58Check = exports.fromBech32 = exports.fromBase58Check = void 0;
-const networks = require('./networks');
-const payments = require('./payments');
-const bscript = require('./script');
-const types = require('./types');
-const bech32_1 = require('bech32');
-const bs58check = require('bs58check');
-const { typeforce } = types;
-const FUTURE_SEGWIT_MAX_SIZE = 40;
-const FUTURE_SEGWIT_MIN_SIZE = 2;
-const FUTURE_SEGWIT_MAX_VERSION = 16;
-const FUTURE_SEGWIT_MIN_VERSION = 1;
-const FUTURE_SEGWIT_VERSION_DIFF = 0x50;
-function _toFutureSegwitAddress(output, network) {
- const data = output.slice(2);
- if (
- data.length < FUTURE_SEGWIT_MIN_SIZE ||
- data.length > FUTURE_SEGWIT_MAX_SIZE
- )
- throw new TypeError('Invalid program length for segwit address');
- const version = output[0] - FUTURE_SEGWIT_VERSION_DIFF;
- if (
- version < FUTURE_SEGWIT_MIN_VERSION ||
- version > FUTURE_SEGWIT_MAX_VERSION
- )
- throw new TypeError('Invalid version for segwit address');
- if (output[1] !== data.length)
- throw new TypeError('Invalid script for segwit address');
- return toBech32(data, version, network.bech32);
-}
-function fromBase58Check(address) {
- const payload = bs58check.decode(address);
- // TODO: 4.0.0, move to "toOutputScript"
- if (payload.length < 21) throw new TypeError(address + ' is too short');
- if (payload.length > 21) throw new TypeError(address + ' is too long');
- const version = payload.readUInt8(0);
- const hash = payload.slice(1);
- return { version, hash };
-}
-exports.fromBase58Check = fromBase58Check;
-function fromBech32(address) {
- let result;
- let version;
- try {
- result = bech32_1.bech32.decode(address);
- } catch (e) {}
- if (result) {
- version = result.words[0];
- if (version !== 0) throw new TypeError(address + ' uses wrong encoding');
- } else {
- result = bech32_1.bech32m.decode(address);
- version = result.words[0];
- if (version === 0) throw new TypeError(address + ' uses wrong encoding');
- }
- const data = bech32_1.bech32.fromWords(result.words.slice(1));
- return {
- version,
- prefix: result.prefix,
- data: Buffer.from(data),
- };
-}
-exports.fromBech32 = fromBech32;
-function toBase58Check(hash, version) {
- typeforce(types.tuple(types.Hash160bit, types.UInt8), arguments);
- const payload = Buffer.allocUnsafe(21);
- payload.writeUInt8(version, 0);
- hash.copy(payload, 1);
- return bs58check.encode(payload);
-}
-exports.toBase58Check = toBase58Check;
-function toBech32(data, version, prefix) {
- const words = bech32_1.bech32.toWords(data);
- words.unshift(version);
- return version === 0
- ? bech32_1.bech32.encode(prefix, words)
- : bech32_1.bech32m.encode(prefix, words);
-}
-exports.toBech32 = toBech32;
-function fromOutputScript(output, network) {
- // TODO: Network
- network = network || networks.bitcoin;
- try {
- return payments.p2pkh({ output, network }).address;
- } catch (e) {}
- try {
- return payments.p2sh({ output, network }).address;
- } catch (e) {}
- try {
- return payments.p2wpkh({ output, network }).address;
- } catch (e) {}
- try {
- return payments.p2wsh({ output, network }).address;
- } catch (e) {}
- try {
- return _toFutureSegwitAddress(output, network);
- } catch (e) {}
- throw new Error(bscript.toASM(output) + ' has no matching Address');
-}
-exports.fromOutputScript = fromOutputScript;
-function toOutputScript(address, network) {
- network = network || networks.bitcoin;
- let decodeBase58;
- let decodeBech32;
- try {
- decodeBase58 = fromBase58Check(address);
- } catch (e) {}
- if (decodeBase58) {
- if (decodeBase58.version === network.pubKeyHash)
- return payments.p2pkh({ hash: decodeBase58.hash }).output;
- if (decodeBase58.version === network.scriptHash)
- return payments.p2sh({ hash: decodeBase58.hash }).output;
- } else {
- try {
- decodeBech32 = fromBech32(address);
- } catch (e) {}
- if (decodeBech32) {
- if (decodeBech32.prefix !== network.bech32)
- throw new Error(address + ' has an invalid prefix');
- if (decodeBech32.version === 0) {
- if (decodeBech32.data.length === 20)
- return payments.p2wpkh({ hash: decodeBech32.data }).output;
- if (decodeBech32.data.length === 32)
- return payments.p2wsh({ hash: decodeBech32.data }).output;
- } else if (
- decodeBech32.version >= FUTURE_SEGWIT_MIN_VERSION &&
- decodeBech32.version <= FUTURE_SEGWIT_MAX_VERSION &&
- decodeBech32.data.length >= FUTURE_SEGWIT_MIN_SIZE &&
- decodeBech32.data.length <= FUTURE_SEGWIT_MAX_SIZE
- )
- return bscript.compile([
- decodeBech32.version + FUTURE_SEGWIT_VERSION_DIFF,
- decodeBech32.data,
- ]);
- }
- }
- throw new Error(address + ' has no matching Script');
-}
-exports.toOutputScript = toOutputScript;
diff --git a/src/bip66.d.ts b/src/bip66.d.ts
deleted file mode 100644
index 547c57f78..000000000
--- a/src/bip66.d.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-///
-export declare function check(buffer: Buffer): boolean;
-export declare function decode(buffer: Buffer): {
- r: Buffer;
- s: Buffer;
-};
-export declare function encode(r: Buffer, s: Buffer): Buffer;
diff --git a/src/bufferutils.d.ts b/src/bufferutils.d.ts
deleted file mode 100644
index b1d89665b..000000000
--- a/src/bufferutils.d.ts
+++ /dev/null
@@ -1,41 +0,0 @@
-///
-import * as varuint from 'varuint-bitcoin';
-export { varuint };
-export declare function readUInt64LE(buffer: Buffer, offset: number): number;
-export declare function writeUInt64LE(buffer: Buffer, value: number, offset: number): number;
-export declare function reverseBuffer(buffer: Buffer): Buffer;
-export declare function cloneBuffer(buffer: Buffer): Buffer;
-/**
- * Helper class for serialization of bitcoin data types into a pre-allocated buffer.
- */
-export declare class BufferWriter {
- buffer: Buffer;
- offset: number;
- static withCapacity(size: number): BufferWriter;
- constructor(buffer: Buffer, offset?: number);
- writeUInt8(i: number): void;
- writeInt32(i: number): void;
- writeUInt32(i: number): void;
- writeUInt64(i: number): void;
- writeVarInt(i: number): void;
- writeSlice(slice: Buffer): void;
- writeVarSlice(slice: Buffer): void;
- writeVector(vector: Buffer[]): void;
- end(): Buffer;
-}
-/**
- * Helper class for reading of bitcoin data types from a buffer.
- */
-export declare class BufferReader {
- buffer: Buffer;
- offset: number;
- constructor(buffer: Buffer, offset?: number);
- readUInt8(): number;
- readInt32(): number;
- readUInt32(): number;
- readUInt64(): number;
- readVarInt(): number;
- readSlice(n: number): Buffer;
- readVarSlice(): Buffer;
- readVector(): Buffer[];
-}
diff --git a/src/bufferutils.js b/src/bufferutils.js
deleted file mode 100644
index 83a013b34..000000000
--- a/src/bufferutils.js
+++ /dev/null
@@ -1,154 +0,0 @@
-'use strict';
-Object.defineProperty(exports, '__esModule', { value: true });
-exports.BufferReader = exports.BufferWriter = exports.cloneBuffer = exports.reverseBuffer = exports.writeUInt64LE = exports.readUInt64LE = exports.varuint = void 0;
-const types = require('./types');
-const { typeforce } = types;
-const varuint = require('varuint-bitcoin');
-exports.varuint = varuint;
-// https://github.com/feross/buffer/blob/master/index.js#L1127
-function verifuint(value, max) {
- if (typeof value !== 'number')
- throw new Error('cannot write a non-number as a number');
- if (value < 0)
- throw new Error('specified a negative value for writing an unsigned value');
- if (value > max) throw new Error('RangeError: value out of range');
- if (Math.floor(value) !== value)
- throw new Error('value has a fractional component');
-}
-function readUInt64LE(buffer, offset) {
- const a = buffer.readUInt32LE(offset);
- let b = buffer.readUInt32LE(offset + 4);
- b *= 0x100000000;
- verifuint(b + a, 0x001fffffffffffff);
- return b + a;
-}
-exports.readUInt64LE = readUInt64LE;
-function writeUInt64LE(buffer, value, offset) {
- verifuint(value, 0x001fffffffffffff);
- buffer.writeInt32LE(value & -1, offset);
- buffer.writeUInt32LE(Math.floor(value / 0x100000000), offset + 4);
- return offset + 8;
-}
-exports.writeUInt64LE = writeUInt64LE;
-function reverseBuffer(buffer) {
- if (buffer.length < 1) return buffer;
- let j = buffer.length - 1;
- let tmp = 0;
- for (let i = 0; i < buffer.length / 2; i++) {
- tmp = buffer[i];
- buffer[i] = buffer[j];
- buffer[j] = tmp;
- j--;
- }
- return buffer;
-}
-exports.reverseBuffer = reverseBuffer;
-function cloneBuffer(buffer) {
- const clone = Buffer.allocUnsafe(buffer.length);
- buffer.copy(clone);
- return clone;
-}
-exports.cloneBuffer = cloneBuffer;
-/**
- * Helper class for serialization of bitcoin data types into a pre-allocated buffer.
- */
-class BufferWriter {
- constructor(buffer, offset = 0) {
- this.buffer = buffer;
- this.offset = offset;
- typeforce(types.tuple(types.Buffer, types.UInt32), [buffer, offset]);
- }
- static withCapacity(size) {
- return new BufferWriter(Buffer.alloc(size));
- }
- writeUInt8(i) {
- this.offset = this.buffer.writeUInt8(i, this.offset);
- }
- writeInt32(i) {
- this.offset = this.buffer.writeInt32LE(i, this.offset);
- }
- writeUInt32(i) {
- this.offset = this.buffer.writeUInt32LE(i, this.offset);
- }
- writeUInt64(i) {
- this.offset = writeUInt64LE(this.buffer, i, this.offset);
- }
- writeVarInt(i) {
- varuint.encode(i, this.buffer, this.offset);
- this.offset += varuint.encode.bytes;
- }
- writeSlice(slice) {
- if (this.buffer.length < this.offset + slice.length) {
- throw new Error('Cannot write slice out of bounds');
- }
- this.offset += slice.copy(this.buffer, this.offset);
- }
- writeVarSlice(slice) {
- this.writeVarInt(slice.length);
- this.writeSlice(slice);
- }
- writeVector(vector) {
- this.writeVarInt(vector.length);
- vector.forEach(buf => this.writeVarSlice(buf));
- }
- end() {
- if (this.buffer.length === this.offset) {
- return this.buffer;
- }
- throw new Error(`buffer size ${this.buffer.length}, offset ${this.offset}`);
- }
-}
-exports.BufferWriter = BufferWriter;
-/**
- * Helper class for reading of bitcoin data types from a buffer.
- */
-class BufferReader {
- constructor(buffer, offset = 0) {
- this.buffer = buffer;
- this.offset = offset;
- typeforce(types.tuple(types.Buffer, types.UInt32), [buffer, offset]);
- }
- readUInt8() {
- const result = this.buffer.readUInt8(this.offset);
- this.offset++;
- return result;
- }
- readInt32() {
- const result = this.buffer.readInt32LE(this.offset);
- this.offset += 4;
- return result;
- }
- readUInt32() {
- const result = this.buffer.readUInt32LE(this.offset);
- this.offset += 4;
- return result;
- }
- readUInt64() {
- const result = readUInt64LE(this.buffer, this.offset);
- this.offset += 8;
- return result;
- }
- readVarInt() {
- const vi = varuint.decode(this.buffer, this.offset);
- this.offset += varuint.decode.bytes;
- return vi;
- }
- readSlice(n) {
- if (this.buffer.length < this.offset + n) {
- throw new Error('Cannot read slice out of bounds');
- }
- const result = this.buffer.slice(this.offset, this.offset + n);
- this.offset += n;
- return result;
- }
- readVarSlice() {
- return this.readSlice(this.readVarInt());
- }
- readVector() {
- const count = this.readVarInt();
- const vector = [];
- for (let i = 0; i < count; i++) vector.push(this.readVarSlice());
- return vector;
- }
-}
-exports.BufferReader = BufferReader;
diff --git a/src/cjs/address.cjs b/src/cjs/address.cjs
new file mode 100644
index 000000000..78f42ef4c
--- /dev/null
+++ b/src/cjs/address.cjs
@@ -0,0 +1,260 @@
+'use strict';
+var __createBinding =
+ (this && this.__createBinding) ||
+ (Object.create
+ ? function (o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ var desc = Object.getOwnPropertyDescriptor(m, k);
+ if (
+ !desc ||
+ ('get' in desc ? !m.__esModule : desc.writable || desc.configurable)
+ ) {
+ desc = {
+ enumerable: true,
+ get: function () {
+ return m[k];
+ },
+ };
+ }
+ Object.defineProperty(o, k2, desc);
+ }
+ : function (o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ o[k2] = m[k];
+ });
+var __setModuleDefault =
+ (this && this.__setModuleDefault) ||
+ (Object.create
+ ? function (o, v) {
+ Object.defineProperty(o, 'default', { enumerable: true, value: v });
+ }
+ : function (o, v) {
+ o['default'] = v;
+ });
+var __importStar =
+ (this && this.__importStar) ||
+ function (mod) {
+ if (mod && mod.__esModule) return mod;
+ var result = {};
+ if (mod != null)
+ for (var k in mod)
+ if (k !== 'default' && Object.prototype.hasOwnProperty.call(mod, k))
+ __createBinding(result, mod, k);
+ __setModuleDefault(result, mod);
+ return result;
+ };
+var __importDefault =
+ (this && this.__importDefault) ||
+ function (mod) {
+ return mod && mod.__esModule ? mod : { default: mod };
+ };
+Object.defineProperty(exports, '__esModule', { value: true });
+exports.fromBase58Check = fromBase58Check;
+exports.fromBech32 = fromBech32;
+exports.toBase58Check = toBase58Check;
+exports.toBech32 = toBech32;
+exports.fromOutputScript = fromOutputScript;
+exports.toOutputScript = toOutputScript;
+const networks = __importStar(require('./networks.cjs'));
+const payments = __importStar(require('./payments/index.cjs'));
+const bscript = __importStar(require('./script.cjs'));
+const types_js_1 = require('./types.cjs');
+const bech32_1 = require('bech32');
+const bs58check_1 = __importDefault(require('bs58check'));
+const tools = __importStar(require('uint8array-tools'));
+const v = __importStar(require('valibot'));
+const FUTURE_SEGWIT_MAX_SIZE = 40;
+const FUTURE_SEGWIT_MIN_SIZE = 2;
+const FUTURE_SEGWIT_MAX_VERSION = 16;
+const FUTURE_SEGWIT_MIN_VERSION = 2;
+const FUTURE_SEGWIT_VERSION_DIFF = 0x50;
+const FUTURE_SEGWIT_VERSION_WARNING =
+ 'WARNING: Sending to a future segwit version address can lead to loss of funds. ' +
+ 'End users MUST be warned carefully in the GUI and asked if they wish to proceed ' +
+ 'with caution. Wallets should verify the segwit version from the output of fromBech32, ' +
+ 'then decide when it is safe to use which version of segwit.';
+const WARNING_STATES = [false, false];
+/**
+ * Converts an output buffer to a future segwit address.
+ * @param output - The output buffer.
+ * @param network - The network object.
+ * @returns The future segwit address.
+ * @throws {TypeError} If the program length or version is invalid for segwit address.
+ */
+function _toFutureSegwitAddress(output, network) {
+ const data = output.slice(2);
+ if (
+ data.length < FUTURE_SEGWIT_MIN_SIZE ||
+ data.length > FUTURE_SEGWIT_MAX_SIZE
+ )
+ throw new TypeError('Invalid program length for segwit address');
+ const version = output[0] - FUTURE_SEGWIT_VERSION_DIFF;
+ if (
+ version < FUTURE_SEGWIT_MIN_VERSION ||
+ version > FUTURE_SEGWIT_MAX_VERSION
+ )
+ throw new TypeError('Invalid version for segwit address');
+ if (output[1] !== data.length)
+ throw new TypeError('Invalid script for segwit address');
+ if (WARNING_STATES[0] === false) {
+ console.warn(FUTURE_SEGWIT_VERSION_WARNING);
+ WARNING_STATES[0] = true;
+ }
+ return toBech32(data, version, network.bech32);
+}
+/**
+ * Decodes a base58check encoded Bitcoin address and returns the version and hash.
+ *
+ * @param address - The base58check encoded Bitcoin address to decode.
+ * @returns An object containing the version and hash of the decoded address.
+ * @throws {TypeError} If the address is too short or too long.
+ */
+function fromBase58Check(address) {
+ const payload = bs58check_1.default.decode(address);
+ // TODO: 4.0.0, move to "toOutputScript"
+ if (payload.length < 21) throw new TypeError(address + ' is too short');
+ if (payload.length > 21) throw new TypeError(address + ' is too long');
+ const version = tools.readUInt8(payload, 0);
+ const hash = payload.slice(1);
+ return { version, hash };
+}
+/**
+ * Converts a Bech32 or Bech32m encoded address to its corresponding data representation.
+ * @param address - The Bech32 or Bech32m encoded address.
+ * @returns An object containing the version, prefix, and data of the address.
+ * @throws {TypeError} If the address uses the wrong encoding.
+ */
+function fromBech32(address) {
+ let result;
+ let version;
+ try {
+ result = bech32_1.bech32.decode(address);
+ } catch (e) {}
+ if (result) {
+ version = result.words[0];
+ if (version !== 0) throw new TypeError(address + ' uses wrong encoding');
+ } else {
+ result = bech32_1.bech32m.decode(address);
+ version = result.words[0];
+ if (version === 0) throw new TypeError(address + ' uses wrong encoding');
+ }
+ const data = bech32_1.bech32.fromWords(result.words.slice(1));
+ return {
+ version,
+ prefix: result.prefix,
+ data: Uint8Array.from(data),
+ };
+}
+/**
+ * Converts a hash to a Base58Check-encoded string.
+ * @param hash - The hash to be encoded.
+ * @param version - The version byte to be prepended to the encoded string.
+ * @returns The Base58Check-encoded string.
+ */
+function toBase58Check(hash, version) {
+ v.parse(v.tuple([types_js_1.Hash160bitSchema, types_js_1.UInt8Schema]), [
+ hash,
+ version,
+ ]);
+ const payload = new Uint8Array(21);
+ tools.writeUInt8(payload, 0, version);
+ payload.set(hash, 1);
+ return bs58check_1.default.encode(payload);
+}
+/**
+ * Converts a buffer to a Bech32 or Bech32m encoded string.
+ * @param data - The buffer to be encoded.
+ * @param version - The version number to be used in the encoding.
+ * @param prefix - The prefix string to be used in the encoding.
+ * @returns The Bech32 or Bech32m encoded string.
+ */
+function toBech32(data, version, prefix) {
+ const words = bech32_1.bech32.toWords(data);
+ words.unshift(version);
+ return version === 0
+ ? bech32_1.bech32.encode(prefix, words)
+ : bech32_1.bech32m.encode(prefix, words);
+}
+/**
+ * Converts an output script to a Bitcoin address.
+ * @param output - The output script as a Buffer.
+ * @param network - The Bitcoin network (optional).
+ * @returns The Bitcoin address corresponding to the output script.
+ * @throws If the output script has no matching address.
+ */
+function fromOutputScript(output, network) {
+ // TODO: Network
+ network = network || networks.bitcoin;
+ try {
+ return payments.p2pkh({ output, network }).address;
+ } catch (e) {}
+ try {
+ return payments.p2sh({ output, network }).address;
+ } catch (e) {}
+ try {
+ return payments.p2wpkh({ output, network }).address;
+ } catch (e) {}
+ try {
+ return payments.p2wsh({ output, network }).address;
+ } catch (e) {}
+ try {
+ return payments.p2tr({ output, network }).address;
+ } catch (e) {}
+ try {
+ return _toFutureSegwitAddress(output, network);
+ } catch (e) {}
+ throw new Error(bscript.toASM(output) + ' has no matching Address');
+}
+/**
+ * Converts a Bitcoin address to its corresponding output script.
+ * @param address - The Bitcoin address to convert.
+ * @param network - The Bitcoin network to use. Defaults to the Bitcoin network.
+ * @returns The corresponding output script as a Buffer.
+ * @throws If the address has an invalid prefix or no matching script.
+ */
+function toOutputScript(address, network) {
+ network = network || networks.bitcoin;
+ let decodeBase58;
+ let decodeBech32;
+ try {
+ decodeBase58 = fromBase58Check(address);
+ } catch (e) {}
+ if (decodeBase58) {
+ if (decodeBase58.version === network.pubKeyHash)
+ return payments.p2pkh({ hash: decodeBase58.hash }).output;
+ if (decodeBase58.version === network.scriptHash)
+ return payments.p2sh({ hash: decodeBase58.hash }).output;
+ } else {
+ try {
+ decodeBech32 = fromBech32(address);
+ } catch (e) {}
+ if (decodeBech32) {
+ if (decodeBech32.prefix !== network.bech32)
+ throw new Error(address + ' has an invalid prefix');
+ if (decodeBech32.version === 0) {
+ if (decodeBech32.data.length === 20)
+ return payments.p2wpkh({ hash: decodeBech32.data }).output;
+ if (decodeBech32.data.length === 32)
+ return payments.p2wsh({ hash: decodeBech32.data }).output;
+ } else if (decodeBech32.version === 1) {
+ if (decodeBech32.data.length === 32)
+ return payments.p2tr({ pubkey: decodeBech32.data }).output;
+ } else if (
+ decodeBech32.version >= FUTURE_SEGWIT_MIN_VERSION &&
+ decodeBech32.version <= FUTURE_SEGWIT_MAX_VERSION &&
+ decodeBech32.data.length >= FUTURE_SEGWIT_MIN_SIZE &&
+ decodeBech32.data.length <= FUTURE_SEGWIT_MAX_SIZE
+ ) {
+ if (WARNING_STATES[1] === false) {
+ console.warn(FUTURE_SEGWIT_VERSION_WARNING);
+ WARNING_STATES[1] = true;
+ }
+ return bscript.compile([
+ decodeBech32.version + FUTURE_SEGWIT_VERSION_DIFF,
+ decodeBech32.data,
+ ]);
+ }
+ }
+ }
+ throw new Error(address + ' has no matching Script');
+}
diff --git a/src/cjs/address.d.ts b/src/cjs/address.d.ts
new file mode 100644
index 000000000..06229d7f8
--- /dev/null
+++ b/src/cjs/address.d.ts
@@ -0,0 +1,72 @@
+/**
+ * bitcoin address decode and encode tools, include base58、bech32 and output script
+ *
+ * networks support bitcoin、bitcoin testnet and bitcoin regtest
+ *
+ * addresses support P2PKH、P2SH、P2WPKH、P2WSH、P2TR and so on
+ *
+ * @packageDocumentation
+ */
+import { Network } from './networks.js';
+/** base58check decode result */
+export interface Base58CheckResult {
+ /** address hash */
+ hash: Uint8Array;
+ /** address version: 0x00 for P2PKH, 0x05 for P2SH */
+ version: number;
+}
+/** bech32 decode result */
+export interface Bech32Result {
+ /** address version: 0x00 for P2WPKH、P2WSH, 0x01 for P2TR*/
+ version: number;
+ /** address prefix: bc for P2WPKH、P2WSH、P2TR */
+ prefix: string;
+ /** address data:20 bytes for P2WPKH, 32 bytes for P2WSH、P2TR */
+ data: Uint8Array;
+}
+/**
+ * Decodes a base58check encoded Bitcoin address and returns the version and hash.
+ *
+ * @param address - The base58check encoded Bitcoin address to decode.
+ * @returns An object containing the version and hash of the decoded address.
+ * @throws {TypeError} If the address is too short or too long.
+ */
+export declare function fromBase58Check(address: string): Base58CheckResult;
+/**
+ * Converts a Bech32 or Bech32m encoded address to its corresponding data representation.
+ * @param address - The Bech32 or Bech32m encoded address.
+ * @returns An object containing the version, prefix, and data of the address.
+ * @throws {TypeError} If the address uses the wrong encoding.
+ */
+export declare function fromBech32(address: string): Bech32Result;
+/**
+ * Converts a hash to a Base58Check-encoded string.
+ * @param hash - The hash to be encoded.
+ * @param version - The version byte to be prepended to the encoded string.
+ * @returns The Base58Check-encoded string.
+ */
+export declare function toBase58Check(hash: Uint8Array, version: number): string;
+/**
+ * Converts a buffer to a Bech32 or Bech32m encoded string.
+ * @param data - The buffer to be encoded.
+ * @param version - The version number to be used in the encoding.
+ * @param prefix - The prefix string to be used in the encoding.
+ * @returns The Bech32 or Bech32m encoded string.
+ */
+export declare function toBech32(data: Uint8Array, version: number, prefix: string): string;
+/**
+ * Converts an output script to a Bitcoin address.
+ * @param output - The output script as a Buffer.
+ * @param network - The Bitcoin network (optional).
+ * @returns The Bitcoin address corresponding to the output script.
+ * @throws If the output script has no matching address.
+ */
+export declare function fromOutputScript(output: Uint8Array, network?: Network): string;
+/**
+ * Converts a Bitcoin address to its corresponding output script.
+ * @param address - The Bitcoin address to convert.
+ * @param network - The Bitcoin network to use. Defaults to the Bitcoin network.
+ * @returns The corresponding output script as a Buffer.
+ * @throws If the address has an invalid prefix or no matching script.
+ */
+export declare function toOutputScript(address: string, network?: Network): Uint8Array;
diff --git a/src/cjs/bip66.cjs b/src/cjs/bip66.cjs
new file mode 100644
index 000000000..b1d354591
--- /dev/null
+++ b/src/cjs/bip66.cjs
@@ -0,0 +1,115 @@
+'use strict';
+// Reference https://github.com/bitcoin/bips/blob/master/bip-0066.mediawiki
+// Format: 0x30 [total-length] 0x02 [R-length] [R] 0x02 [S-length] [S]
+// NOTE: SIGHASH byte ignored AND restricted, truncate before use
+Object.defineProperty(exports, '__esModule', { value: true });
+exports.check = check;
+exports.decode = decode;
+exports.encode = encode;
+/**
+ * Checks if the given buffer is a valid BIP66-encoded signature.
+ *
+ * @param buffer - The buffer to check.
+ * @returns A boolean indicating whether the buffer is a valid BIP66-encoded signature.
+ */
+function check(buffer) {
+ if (buffer.length < 8) return false;
+ if (buffer.length > 72) return false;
+ if (buffer[0] !== 0x30) return false;
+ if (buffer[1] !== buffer.length - 2) return false;
+ if (buffer[2] !== 0x02) return false;
+ const lenR = buffer[3];
+ if (lenR === 0) return false;
+ if (5 + lenR >= buffer.length) return false;
+ if (buffer[4 + lenR] !== 0x02) return false;
+ const lenS = buffer[5 + lenR];
+ if (lenS === 0) return false;
+ if (6 + lenR + lenS !== buffer.length) return false;
+ if (buffer[4] & 0x80) return false;
+ if (lenR > 1 && buffer[4] === 0x00 && !(buffer[5] & 0x80)) return false;
+ if (buffer[lenR + 6] & 0x80) return false;
+ if (lenS > 1 && buffer[lenR + 6] === 0x00 && !(buffer[lenR + 7] & 0x80))
+ return false;
+ return true;
+}
+/**
+ * Decodes a DER-encoded signature buffer and returns the R and S values.
+ * @param buffer - The DER-encoded signature buffer.
+ * @returns An object containing the R and S values.
+ * @throws {Error} If the DER sequence length is too short, too long, or invalid.
+ * @throws {Error} If the R or S length is zero or invalid.
+ * @throws {Error} If the R or S value is negative or excessively padded.
+ */
+function decode(buffer) {
+ if (buffer.length < 8) throw new Error('DER sequence length is too short');
+ if (buffer.length > 72) throw new Error('DER sequence length is too long');
+ if (buffer[0] !== 0x30) throw new Error('Expected DER sequence');
+ if (buffer[1] !== buffer.length - 2)
+ throw new Error('DER sequence length is invalid');
+ if (buffer[2] !== 0x02) throw new Error('Expected DER integer');
+ const lenR = buffer[3];
+ if (lenR === 0) throw new Error('R length is zero');
+ if (5 + lenR >= buffer.length) throw new Error('R length is too long');
+ if (buffer[4 + lenR] !== 0x02) throw new Error('Expected DER integer (2)');
+ const lenS = buffer[5 + lenR];
+ if (lenS === 0) throw new Error('S length is zero');
+ if (6 + lenR + lenS !== buffer.length) throw new Error('S length is invalid');
+ if (buffer[4] & 0x80) throw new Error('R value is negative');
+ if (lenR > 1 && buffer[4] === 0x00 && !(buffer[5] & 0x80))
+ throw new Error('R value excessively padded');
+ if (buffer[lenR + 6] & 0x80) throw new Error('S value is negative');
+ if (lenS > 1 && buffer[lenR + 6] === 0x00 && !(buffer[lenR + 7] & 0x80))
+ throw new Error('S value excessively padded');
+ // non-BIP66 - extract R, S values
+ return {
+ r: buffer.slice(4, 4 + lenR),
+ s: buffer.slice(6 + lenR),
+ };
+}
+/*
+ * Expects r and s to be positive DER integers.
+ *
+ * The DER format uses the most significant bit as a sign bit (& 0x80).
+ * If the significant bit is set AND the integer is positive, a 0x00 is prepended.
+ *
+ * Examples:
+ *
+ * 0 => 0x00
+ * 1 => 0x01
+ * -1 => 0xff
+ * 127 => 0x7f
+ * -127 => 0x81
+ * 128 => 0x0080
+ * -128 => 0x80
+ * 255 => 0x00ff
+ * -255 => 0xff01
+ * 16300 => 0x3fac
+ * -16300 => 0xc054
+ * 62300 => 0x00f35c
+ * -62300 => 0xff0ca4
+ */
+function encode(r, s) {
+ const lenR = r.length;
+ const lenS = s.length;
+ if (lenR === 0) throw new Error('R length is zero');
+ if (lenS === 0) throw new Error('S length is zero');
+ if (lenR > 33) throw new Error('R length is too long');
+ if (lenS > 33) throw new Error('S length is too long');
+ if (r[0] & 0x80) throw new Error('R value is negative');
+ if (s[0] & 0x80) throw new Error('S value is negative');
+ if (lenR > 1 && r[0] === 0x00 && !(r[1] & 0x80))
+ throw new Error('R value excessively padded');
+ if (lenS > 1 && s[0] === 0x00 && !(s[1] & 0x80))
+ throw new Error('S value excessively padded');
+ const signature = new Uint8Array(6 + lenR + lenS);
+ // 0x30 [total-length] 0x02 [R-length] [R] 0x02 [S-length] [S]
+ signature[0] = 0x30;
+ signature[1] = signature.length - 2;
+ signature[2] = 0x02;
+ signature[3] = r.length;
+ signature.set(r, 4);
+ signature[4 + lenR] = 0x02;
+ signature[5 + lenR] = s.length;
+ signature.set(s, 6 + lenR);
+ return signature;
+}
diff --git a/src/cjs/bip66.d.ts b/src/cjs/bip66.d.ts
new file mode 100644
index 000000000..b7372e6af
--- /dev/null
+++ b/src/cjs/bip66.d.ts
@@ -0,0 +1,20 @@
+/**
+ * Checks if the given buffer is a valid BIP66-encoded signature.
+ *
+ * @param buffer - The buffer to check.
+ * @returns A boolean indicating whether the buffer is a valid BIP66-encoded signature.
+ */
+export declare function check(buffer: Uint8Array): boolean;
+/**
+ * Decodes a DER-encoded signature buffer and returns the R and S values.
+ * @param buffer - The DER-encoded signature buffer.
+ * @returns An object containing the R and S values.
+ * @throws {Error} If the DER sequence length is too short, too long, or invalid.
+ * @throws {Error} If the R or S length is zero or invalid.
+ * @throws {Error} If the R or S value is negative or excessively padded.
+ */
+export declare function decode(buffer: Uint8Array): {
+ r: Uint8Array;
+ s: Uint8Array;
+};
+export declare function encode(r: Uint8Array, s: Uint8Array): Uint8Array;
diff --git a/src/cjs/block.cjs b/src/cjs/block.cjs
new file mode 100644
index 000000000..bc4db559c
--- /dev/null
+++ b/src/cjs/block.cjs
@@ -0,0 +1,268 @@
+'use strict';
+var __createBinding =
+ (this && this.__createBinding) ||
+ (Object.create
+ ? function (o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ var desc = Object.getOwnPropertyDescriptor(m, k);
+ if (
+ !desc ||
+ ('get' in desc ? !m.__esModule : desc.writable || desc.configurable)
+ ) {
+ desc = {
+ enumerable: true,
+ get: function () {
+ return m[k];
+ },
+ };
+ }
+ Object.defineProperty(o, k2, desc);
+ }
+ : function (o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ o[k2] = m[k];
+ });
+var __setModuleDefault =
+ (this && this.__setModuleDefault) ||
+ (Object.create
+ ? function (o, v) {
+ Object.defineProperty(o, 'default', { enumerable: true, value: v });
+ }
+ : function (o, v) {
+ o['default'] = v;
+ });
+var __importStar =
+ (this && this.__importStar) ||
+ function (mod) {
+ if (mod && mod.__esModule) return mod;
+ var result = {};
+ if (mod != null)
+ for (var k in mod)
+ if (k !== 'default' && Object.prototype.hasOwnProperty.call(mod, k))
+ __createBinding(result, mod, k);
+ __setModuleDefault(result, mod);
+ return result;
+ };
+Object.defineProperty(exports, '__esModule', { value: true });
+exports.Block = void 0;
+const bufferutils_js_1 = require('./bufferutils.cjs');
+const bcrypto = __importStar(require('./crypto.cjs'));
+const merkle_js_1 = require('./merkle.cjs');
+const transaction_js_1 = require('./transaction.cjs');
+const v = __importStar(require('valibot'));
+const tools = __importStar(require('uint8array-tools'));
+const errorMerkleNoTxes = new TypeError(
+ 'Cannot compute merkle root for zero transactions',
+);
+const errorWitnessNotSegwit = new TypeError(
+ 'Cannot compute witness commit for non-segwit block',
+);
+class Block {
+ static fromBuffer(buffer) {
+ if (buffer.length < 80) throw new Error('Buffer too small (< 80 bytes)');
+ const bufferReader = new bufferutils_js_1.BufferReader(buffer);
+ const block = new Block();
+ block.version = bufferReader.readInt32();
+ block.prevHash = bufferReader.readSlice(32);
+ block.merkleRoot = bufferReader.readSlice(32);
+ block.timestamp = bufferReader.readUInt32();
+ block.bits = bufferReader.readUInt32();
+ block.nonce = bufferReader.readUInt32();
+ if (buffer.length === 80) return block;
+ const readTransaction = () => {
+ const tx = transaction_js_1.Transaction.fromBuffer(
+ bufferReader.buffer.slice(bufferReader.offset),
+ true,
+ );
+ bufferReader.offset += tx.byteLength();
+ return tx;
+ };
+ const nTransactions = bufferReader.readVarInt();
+ block.transactions = [];
+ for (let i = 0; i < nTransactions; ++i) {
+ const tx = readTransaction();
+ block.transactions.push(tx);
+ }
+ const witnessCommit = block.getWitnessCommit();
+ // This Block contains a witness commit
+ if (witnessCommit) block.witnessCommit = witnessCommit;
+ return block;
+ }
+ static fromHex(hex) {
+ return Block.fromBuffer(tools.fromHex(hex));
+ }
+ static calculateTarget(bits) {
+ const exponent = ((bits & 0xff000000) >> 24) - 3;
+ const mantissa = bits & 0x007fffff;
+ const target = new Uint8Array(32);
+ target[29 - exponent] = (mantissa >> 16) & 0xff;
+ target[30 - exponent] = (mantissa >> 8) & 0xff;
+ target[31 - exponent] = mantissa & 0xff;
+ return target;
+ }
+ static calculateMerkleRoot(transactions, forWitness) {
+ v.parse(v.array(v.object({ getHash: v.function() })), transactions);
+ if (transactions.length === 0) throw errorMerkleNoTxes;
+ if (forWitness && !txesHaveWitnessCommit(transactions))
+ throw errorWitnessNotSegwit;
+ const hashes = transactions.map(transaction =>
+ transaction.getHash(forWitness),
+ );
+ const rootHash = (0, merkle_js_1.fastMerkleRoot)(hashes, bcrypto.hash256);
+ return forWitness
+ ? bcrypto.hash256(
+ tools.concat([rootHash, transactions[0].ins[0].witness[0]]),
+ )
+ : rootHash;
+ }
+ version = 1;
+ prevHash = undefined;
+ merkleRoot = undefined;
+ timestamp = 0;
+ witnessCommit = undefined;
+ bits = 0;
+ nonce = 0;
+ transactions = undefined;
+ getWitnessCommit() {
+ if (!txesHaveWitnessCommit(this.transactions)) return null;
+ // The merkle root for the witness data is in an OP_RETURN output.
+ // There is no rule for the index of the output, so use filter to find it.
+ // The root is prepended with 0xaa21a9ed so check for 0x6a24aa21a9ed
+ // If multiple commits are found, the output with highest index is assumed.
+ const witnessCommits = this.transactions[0].outs
+ .filter(
+ out =>
+ tools.compare(
+ out.script.slice(0, 6),
+ Uint8Array.from([0x6a, 0x24, 0xaa, 0x21, 0xa9, 0xed]),
+ ) === 0,
+ )
+ .map(out => out.script.slice(6, 38));
+ if (witnessCommits.length === 0) return null;
+ // Use the commit with the highest output (should only be one though)
+ const result = witnessCommits[witnessCommits.length - 1];
+ if (!(result instanceof Uint8Array && result.length === 32)) return null;
+ return result;
+ }
+ hasWitnessCommit() {
+ if (
+ this.witnessCommit instanceof Uint8Array &&
+ this.witnessCommit.length === 32
+ )
+ return true;
+ if (this.getWitnessCommit() !== null) return true;
+ return false;
+ }
+ hasWitness() {
+ return anyTxHasWitness(this.transactions);
+ }
+ weight() {
+ const base = this.byteLength(false, false);
+ const total = this.byteLength(false, true);
+ return base * 3 + total;
+ }
+ byteLength(headersOnly, allowWitness = true) {
+ if (headersOnly || !this.transactions) return 80;
+ return (
+ 80 +
+ bufferutils_js_1.varuint.encodingLength(this.transactions.length) +
+ this.transactions.reduce((a, x) => a + x.byteLength(allowWitness), 0)
+ );
+ }
+ getHash() {
+ return bcrypto.hash256(this.toBuffer(true));
+ }
+ getId() {
+ return tools.toHex((0, bufferutils_js_1.reverseBuffer)(this.getHash()));
+ }
+ getUTCDate() {
+ const date = new Date(0); // epoch
+ date.setUTCSeconds(this.timestamp);
+ return date;
+ }
+ // TODO: buffer, offset compatibility
+ toBuffer(headersOnly) {
+ const buffer = new Uint8Array(this.byteLength(headersOnly));
+ const bufferWriter = new bufferutils_js_1.BufferWriter(buffer);
+ bufferWriter.writeInt32(this.version);
+ bufferWriter.writeSlice(this.prevHash);
+ bufferWriter.writeSlice(this.merkleRoot);
+ bufferWriter.writeUInt32(this.timestamp);
+ bufferWriter.writeUInt32(this.bits);
+ bufferWriter.writeUInt32(this.nonce);
+ if (headersOnly || !this.transactions) return buffer;
+ const { bytes } = bufferutils_js_1.varuint.encode(
+ this.transactions.length,
+ buffer,
+ bufferWriter.offset,
+ );
+ bufferWriter.offset += bytes;
+ this.transactions.forEach(tx => {
+ const txSize = tx.byteLength(); // TODO: extract from toBuffer?
+ tx.toBuffer(buffer, bufferWriter.offset);
+ bufferWriter.offset += txSize;
+ });
+ return buffer;
+ }
+ toHex(headersOnly) {
+ return tools.toHex(this.toBuffer(headersOnly));
+ }
+ checkTxRoots() {
+ // If the Block has segwit transactions but no witness commit,
+ // there's no way it can be valid, so fail the check.
+ const hasWitnessCommit = this.hasWitnessCommit();
+ if (!hasWitnessCommit && this.hasWitness()) return false;
+ return (
+ this.__checkMerkleRoot() &&
+ (hasWitnessCommit ? this.__checkWitnessCommit() : true)
+ );
+ }
+ checkProofOfWork() {
+ const hash = (0, bufferutils_js_1.reverseBuffer)(this.getHash());
+ const target = Block.calculateTarget(this.bits);
+ return tools.compare(hash, target) <= 0;
+ }
+ __checkMerkleRoot() {
+ if (!this.transactions) throw errorMerkleNoTxes;
+ const actualMerkleRoot = Block.calculateMerkleRoot(this.transactions);
+ return tools.compare(this.merkleRoot, actualMerkleRoot) === 0;
+ }
+ __checkWitnessCommit() {
+ if (!this.transactions) throw errorMerkleNoTxes;
+ if (!this.hasWitnessCommit()) throw errorWitnessNotSegwit;
+ const actualWitnessCommit = Block.calculateMerkleRoot(
+ this.transactions,
+ true,
+ );
+ return tools.compare(this.witnessCommit, actualWitnessCommit) === 0;
+ }
+}
+exports.Block = Block;
+function txesHaveWitnessCommit(transactions) {
+ return (
+ transactions instanceof Array &&
+ transactions[0] &&
+ transactions[0].ins &&
+ transactions[0].ins instanceof Array &&
+ transactions[0].ins[0] &&
+ transactions[0].ins[0].witness &&
+ transactions[0].ins[0].witness instanceof Array &&
+ transactions[0].ins[0].witness.length > 0
+ );
+}
+function anyTxHasWitness(transactions) {
+ return (
+ transactions instanceof Array &&
+ transactions.some(
+ tx =>
+ typeof tx === 'object' &&
+ tx.ins instanceof Array &&
+ tx.ins.some(
+ input =>
+ typeof input === 'object' &&
+ input.witness instanceof Array &&
+ input.witness.length > 0,
+ ),
+ )
+ );
+}
diff --git a/src/block.d.ts b/src/cjs/block.d.ts
similarity index 61%
rename from src/block.d.ts
rename to src/cjs/block.d.ts
index 1d90c13c4..8afce2735 100644
--- a/src/block.d.ts
+++ b/src/cjs/block.d.ts
@@ -1,27 +1,26 @@
-///
-import { Transaction } from './transaction';
+import { Transaction } from './transaction.js';
export declare class Block {
- static fromBuffer(buffer: Buffer): Block;
+ static fromBuffer(buffer: Uint8Array): Block;
static fromHex(hex: string): Block;
- static calculateTarget(bits: number): Buffer;
- static calculateMerkleRoot(transactions: Transaction[], forWitness?: boolean): Buffer;
+ static calculateTarget(bits: number): Uint8Array;
+ static calculateMerkleRoot(transactions: Transaction[], forWitness?: boolean): Uint8Array;
version: number;
- prevHash?: Buffer;
- merkleRoot?: Buffer;
+ prevHash?: Uint8Array;
+ merkleRoot?: Uint8Array;
timestamp: number;
- witnessCommit?: Buffer;
+ witnessCommit?: Uint8Array;
bits: number;
nonce: number;
transactions?: Transaction[];
- getWitnessCommit(): Buffer | null;
+ getWitnessCommit(): Uint8Array | null;
hasWitnessCommit(): boolean;
hasWitness(): boolean;
weight(): number;
byteLength(headersOnly?: boolean, allowWitness?: boolean): number;
- getHash(): Buffer;
+ getHash(): Uint8Array;
getId(): string;
getUTCDate(): Date;
- toBuffer(headersOnly?: boolean): Buffer;
+ toBuffer(headersOnly?: boolean): Uint8Array;
toHex(headersOnly?: boolean): string;
checkTxRoots(): boolean;
checkProofOfWork(): boolean;
diff --git a/src/cjs/bufferutils.cjs b/src/cjs/bufferutils.cjs
new file mode 100644
index 000000000..c52453264
--- /dev/null
+++ b/src/cjs/bufferutils.cjs
@@ -0,0 +1,207 @@
+'use strict';
+var __createBinding =
+ (this && this.__createBinding) ||
+ (Object.create
+ ? function (o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ var desc = Object.getOwnPropertyDescriptor(m, k);
+ if (
+ !desc ||
+ ('get' in desc ? !m.__esModule : desc.writable || desc.configurable)
+ ) {
+ desc = {
+ enumerable: true,
+ get: function () {
+ return m[k];
+ },
+ };
+ }
+ Object.defineProperty(o, k2, desc);
+ }
+ : function (o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ o[k2] = m[k];
+ });
+var __setModuleDefault =
+ (this && this.__setModuleDefault) ||
+ (Object.create
+ ? function (o, v) {
+ Object.defineProperty(o, 'default', { enumerable: true, value: v });
+ }
+ : function (o, v) {
+ o['default'] = v;
+ });
+var __importStar =
+ (this && this.__importStar) ||
+ function (mod) {
+ if (mod && mod.__esModule) return mod;
+ var result = {};
+ if (mod != null)
+ for (var k in mod)
+ if (k !== 'default' && Object.prototype.hasOwnProperty.call(mod, k))
+ __createBinding(result, mod, k);
+ __setModuleDefault(result, mod);
+ return result;
+ };
+Object.defineProperty(exports, '__esModule', { value: true });
+exports.BufferReader = exports.BufferWriter = exports.varuint = void 0;
+exports.reverseBuffer = reverseBuffer;
+exports.cloneBuffer = cloneBuffer;
+const types = __importStar(require('./types.cjs'));
+const varuint = __importStar(require('varuint-bitcoin'));
+exports.varuint = varuint;
+const v = __importStar(require('valibot'));
+const tools = __importStar(require('uint8array-tools'));
+const MAX_JS_NUMBER = 0x001fffffffffffff;
+// https://github.com/feross/buffer/blob/master/index.js#L1127
+function verifuint(value, max) {
+ if (typeof value !== 'number' && typeof value !== 'bigint')
+ throw new Error('cannot write a non-number as a number');
+ if (value < 0 && value < BigInt(0))
+ throw new Error('specified a negative value for writing an unsigned value');
+ if (value > max && value > BigInt(max))
+ throw new Error('RangeError: value out of range');
+ if (Math.floor(Number(value)) !== Number(value))
+ throw new Error('value has a fractional component');
+}
+/**
+ * Reverses the order of bytes in a buffer.
+ * @param buffer - The buffer to reverse.
+ * @returns A new buffer with the bytes reversed.
+ */
+function reverseBuffer(buffer) {
+ if (buffer.length < 1) return buffer;
+ let j = buffer.length - 1;
+ let tmp = 0;
+ for (let i = 0; i < buffer.length / 2; i++) {
+ tmp = buffer[i];
+ buffer[i] = buffer[j];
+ buffer[j] = tmp;
+ j--;
+ }
+ return buffer;
+}
+function cloneBuffer(buffer) {
+ const clone = new Uint8Array(buffer.length);
+ clone.set(buffer);
+ return clone;
+}
+/**
+ * Helper class for serialization of bitcoin data types into a pre-allocated buffer.
+ */
+class BufferWriter {
+ buffer;
+ offset;
+ static withCapacity(size) {
+ return new BufferWriter(new Uint8Array(size));
+ }
+ constructor(buffer, offset = 0) {
+ this.buffer = buffer;
+ this.offset = offset;
+ v.parse(v.tuple([types.BufferSchema, types.UInt32Schema]), [
+ buffer,
+ offset,
+ ]);
+ }
+ writeUInt8(i) {
+ this.offset = tools.writeUInt8(this.buffer, this.offset, i);
+ }
+ writeInt32(i) {
+ this.offset = tools.writeInt32(this.buffer, this.offset, i, 'LE');
+ }
+ writeInt64(i) {
+ this.offset = tools.writeInt64(this.buffer, this.offset, BigInt(i), 'LE');
+ }
+ writeUInt32(i) {
+ this.offset = tools.writeUInt32(this.buffer, this.offset, i, 'LE');
+ }
+ writeUInt64(i) {
+ this.offset = tools.writeUInt64(this.buffer, this.offset, BigInt(i), 'LE');
+ }
+ writeVarInt(i) {
+ const { bytes } = varuint.encode(i, this.buffer, this.offset);
+ this.offset += bytes;
+ }
+ writeSlice(slice) {
+ if (this.buffer.length < this.offset + slice.length) {
+ throw new Error('Cannot write slice out of bounds');
+ }
+ this.buffer.set(slice, this.offset);
+ this.offset += slice.length;
+ }
+ writeVarSlice(slice) {
+ this.writeVarInt(slice.length);
+ this.writeSlice(slice);
+ }
+ writeVector(vector) {
+ this.writeVarInt(vector.length);
+ vector.forEach(buf => this.writeVarSlice(buf));
+ }
+ end() {
+ if (this.buffer.length === this.offset) {
+ return this.buffer;
+ }
+ throw new Error(`buffer size ${this.buffer.length}, offset ${this.offset}`);
+ }
+}
+exports.BufferWriter = BufferWriter;
+/**
+ * Helper class for reading of bitcoin data types from a buffer.
+ */
+class BufferReader {
+ buffer;
+ offset;
+ constructor(buffer, offset = 0) {
+ this.buffer = buffer;
+ this.offset = offset;
+ v.parse(v.tuple([types.BufferSchema, types.UInt32Schema]), [
+ buffer,
+ offset,
+ ]);
+ }
+ readUInt8() {
+ const result = tools.readUInt8(this.buffer, this.offset);
+ this.offset++;
+ return result;
+ }
+ readInt32() {
+ const result = tools.readInt32(this.buffer, this.offset, 'LE');
+ this.offset += 4;
+ return result;
+ }
+ readUInt32() {
+ const result = tools.readUInt32(this.buffer, this.offset, 'LE');
+ this.offset += 4;
+ return result;
+ }
+ readInt64() {
+ const result = tools.readInt64(this.buffer, this.offset, 'LE');
+ this.offset += 8;
+ return result;
+ }
+ readVarInt() {
+ const { bigintValue, bytes } = varuint.decode(this.buffer, this.offset);
+ this.offset += bytes;
+ return bigintValue;
+ }
+ readSlice(n) {
+ verifuint(n, MAX_JS_NUMBER);
+ const num = Number(n);
+ if (this.buffer.length < this.offset + num) {
+ throw new Error('Cannot read slice out of bounds');
+ }
+ const result = this.buffer.slice(this.offset, this.offset + num);
+ this.offset += num;
+ return result;
+ }
+ readVarSlice() {
+ return this.readSlice(this.readVarInt());
+ }
+ readVector() {
+ const count = this.readVarInt();
+ const vector = [];
+ for (let i = 0; i < count; i++) vector.push(this.readVarSlice());
+ return vector;
+ }
+}
+exports.BufferReader = BufferReader;
diff --git a/src/cjs/bufferutils.d.ts b/src/cjs/bufferutils.d.ts
new file mode 100644
index 000000000..0998bfa56
--- /dev/null
+++ b/src/cjs/bufferutils.d.ts
@@ -0,0 +1,44 @@
+import * as varuint from 'varuint-bitcoin';
+export { varuint };
+/**
+ * Reverses the order of bytes in a buffer.
+ * @param buffer - The buffer to reverse.
+ * @returns A new buffer with the bytes reversed.
+ */
+export declare function reverseBuffer(buffer: Uint8Array): Uint8Array;
+export declare function cloneBuffer(buffer: Uint8Array): Uint8Array;
+/**
+ * Helper class for serialization of bitcoin data types into a pre-allocated buffer.
+ */
+export declare class BufferWriter {
+ buffer: Uint8Array;
+ offset: number;
+ static withCapacity(size: number): BufferWriter;
+ constructor(buffer: Uint8Array, offset?: number);
+ writeUInt8(i: number): void;
+ writeInt32(i: number): void;
+ writeInt64(i: number | bigint): void;
+ writeUInt32(i: number): void;
+ writeUInt64(i: number | bigint): void;
+ writeVarInt(i: number): void;
+ writeSlice(slice: Uint8Array): void;
+ writeVarSlice(slice: Uint8Array): void;
+ writeVector(vector: Uint8Array[]): void;
+ end(): Uint8Array;
+}
+/**
+ * Helper class for reading of bitcoin data types from a buffer.
+ */
+export declare class BufferReader {
+ buffer: Uint8Array;
+ offset: number;
+ constructor(buffer: Uint8Array, offset?: number);
+ readUInt8(): number;
+ readInt32(): number;
+ readUInt32(): number;
+ readInt64(): bigint;
+ readVarInt(): bigint;
+ readSlice(n: number | bigint): Uint8Array;
+ readVarSlice(): Uint8Array;
+ readVector(): Uint8Array[];
+}
diff --git a/src/cjs/crypto.cjs b/src/cjs/crypto.cjs
new file mode 100644
index 000000000..22ef433f3
--- /dev/null
+++ b/src/cjs/crypto.cjs
@@ -0,0 +1,197 @@
+'use strict';
+var __createBinding =
+ (this && this.__createBinding) ||
+ (Object.create
+ ? function (o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ var desc = Object.getOwnPropertyDescriptor(m, k);
+ if (
+ !desc ||
+ ('get' in desc ? !m.__esModule : desc.writable || desc.configurable)
+ ) {
+ desc = {
+ enumerable: true,
+ get: function () {
+ return m[k];
+ },
+ };
+ }
+ Object.defineProperty(o, k2, desc);
+ }
+ : function (o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ o[k2] = m[k];
+ });
+var __setModuleDefault =
+ (this && this.__setModuleDefault) ||
+ (Object.create
+ ? function (o, v) {
+ Object.defineProperty(o, 'default', { enumerable: true, value: v });
+ }
+ : function (o, v) {
+ o['default'] = v;
+ });
+var __importStar =
+ (this && this.__importStar) ||
+ function (mod) {
+ if (mod && mod.__esModule) return mod;
+ var result = {};
+ if (mod != null)
+ for (var k in mod)
+ if (k !== 'default' && Object.prototype.hasOwnProperty.call(mod, k))
+ __createBinding(result, mod, k);
+ __setModuleDefault(result, mod);
+ return result;
+ };
+Object.defineProperty(exports, '__esModule', { value: true });
+exports.TAGGED_HASH_PREFIXES =
+ exports.TAGS =
+ exports.sha1 =
+ exports.sha256 =
+ exports.ripemd160 =
+ void 0;
+exports.hash160 = hash160;
+exports.hash256 = hash256;
+exports.taggedHash = taggedHash;
+/**
+ * A module for hashing functions.
+ * include ripemd160、sha1、sha256、hash160、hash256、taggedHash
+ *
+ * @packageDocumentation
+ */
+const ripemd160_1 = require('@noble/hashes/ripemd160');
+Object.defineProperty(exports, 'ripemd160', {
+ enumerable: true,
+ get: function () {
+ return ripemd160_1.ripemd160;
+ },
+});
+const sha256_1 = require('@noble/hashes/sha256');
+Object.defineProperty(exports, 'sha256', {
+ enumerable: true,
+ get: function () {
+ return sha256_1.sha256;
+ },
+});
+const tools = __importStar(require('uint8array-tools'));
+var sha1_1 = require('@noble/hashes/sha1');
+Object.defineProperty(exports, 'sha1', {
+ enumerable: true,
+ get: function () {
+ return sha1_1.sha1;
+ },
+});
+/**
+ * Computes the HASH160 (RIPEMD-160 after SHA-256) of the given buffer.
+ *
+ * @param buffer - The input data to be hashed.
+ * @returns The HASH160 of the input buffer.
+ */
+function hash160(buffer) {
+ return (0, ripemd160_1.ripemd160)((0, sha256_1.sha256)(buffer));
+}
+/**
+ * Computes the double SHA-256 hash of the given buffer.
+ *
+ * @param buffer - The input data to be hashed.
+ * @returns The double SHA-256 hash of the input buffer.
+ */
+function hash256(buffer) {
+ return (0, sha256_1.sha256)((0, sha256_1.sha256)(buffer));
+}
+exports.TAGS = [
+ 'BIP0340/challenge',
+ 'BIP0340/aux',
+ 'BIP0340/nonce',
+ 'TapLeaf',
+ 'TapBranch',
+ 'TapSighash',
+ 'TapTweak',
+ 'KeyAgg list',
+ 'KeyAgg coefficient',
+];
+/**
+ * A collection of tagged hash prefixes used in various BIP (Bitcoin Improvement Proposals)
+ * and Taproot-related operations. Each prefix is represented as a `Uint8Array`.
+ *
+ * @constant
+ * @type {TaggedHashPrefixes}
+ *
+ * @property {'BIP0340/challenge'} - Prefix for BIP0340 challenge.
+ * @property {'BIP0340/aux'} - Prefix for BIP0340 auxiliary data.
+ * @property {'BIP0340/nonce'} - Prefix for BIP0340 nonce.
+ * @property {TapLeaf} - Prefix for Taproot leaf.
+ * @property {TapBranch} - Prefix for Taproot branch.
+ * @property {TapSighash} - Prefix for Taproot sighash.
+ * @property {TapTweak} - Prefix for Taproot tweak.
+ * @property {'KeyAgg list'} - Prefix for key aggregation list.
+ * @property {'KeyAgg coefficient'} - Prefix for key aggregation coefficient.
+ */
+exports.TAGGED_HASH_PREFIXES = {
+ 'BIP0340/challenge': Uint8Array.from([
+ 123, 181, 45, 122, 159, 239, 88, 50, 62, 177, 191, 122, 64, 125, 179, 130,
+ 210, 243, 242, 216, 27, 177, 34, 79, 73, 254, 81, 143, 109, 72, 211, 124,
+ 123, 181, 45, 122, 159, 239, 88, 50, 62, 177, 191, 122, 64, 125, 179, 130,
+ 210, 243, 242, 216, 27, 177, 34, 79, 73, 254, 81, 143, 109, 72, 211, 124,
+ ]),
+ 'BIP0340/aux': Uint8Array.from([
+ 241, 239, 78, 94, 192, 99, 202, 218, 109, 148, 202, 250, 157, 152, 126, 160,
+ 105, 38, 88, 57, 236, 193, 31, 151, 45, 119, 165, 46, 216, 193, 204, 144,
+ 241, 239, 78, 94, 192, 99, 202, 218, 109, 148, 202, 250, 157, 152, 126, 160,
+ 105, 38, 88, 57, 236, 193, 31, 151, 45, 119, 165, 46, 216, 193, 204, 144,
+ ]),
+ 'BIP0340/nonce': Uint8Array.from([
+ 7, 73, 119, 52, 167, 155, 203, 53, 91, 155, 140, 125, 3, 79, 18, 28, 244,
+ 52, 215, 62, 247, 45, 218, 25, 135, 0, 97, 251, 82, 191, 235, 47, 7, 73,
+ 119, 52, 167, 155, 203, 53, 91, 155, 140, 125, 3, 79, 18, 28, 244, 52, 215,
+ 62, 247, 45, 218, 25, 135, 0, 97, 251, 82, 191, 235, 47,
+ ]),
+ TapLeaf: Uint8Array.from([
+ 174, 234, 143, 220, 66, 8, 152, 49, 5, 115, 75, 88, 8, 29, 30, 38, 56, 211,
+ 95, 28, 181, 64, 8, 212, 211, 87, 202, 3, 190, 120, 233, 238, 174, 234, 143,
+ 220, 66, 8, 152, 49, 5, 115, 75, 88, 8, 29, 30, 38, 56, 211, 95, 28, 181,
+ 64, 8, 212, 211, 87, 202, 3, 190, 120, 233, 238,
+ ]),
+ TapBranch: Uint8Array.from([
+ 25, 65, 161, 242, 229, 110, 185, 95, 162, 169, 241, 148, 190, 92, 1, 247,
+ 33, 111, 51, 237, 130, 176, 145, 70, 52, 144, 208, 91, 245, 22, 160, 21, 25,
+ 65, 161, 242, 229, 110, 185, 95, 162, 169, 241, 148, 190, 92, 1, 247, 33,
+ 111, 51, 237, 130, 176, 145, 70, 52, 144, 208, 91, 245, 22, 160, 21,
+ ]),
+ TapSighash: Uint8Array.from([
+ 244, 10, 72, 223, 75, 42, 112, 200, 180, 146, 75, 242, 101, 70, 97, 237, 61,
+ 149, 253, 102, 163, 19, 235, 135, 35, 117, 151, 198, 40, 228, 160, 49, 244,
+ 10, 72, 223, 75, 42, 112, 200, 180, 146, 75, 242, 101, 70, 97, 237, 61, 149,
+ 253, 102, 163, 19, 235, 135, 35, 117, 151, 198, 40, 228, 160, 49,
+ ]),
+ TapTweak: Uint8Array.from([
+ 232, 15, 225, 99, 156, 156, 160, 80, 227, 175, 27, 57, 193, 67, 198, 62, 66,
+ 156, 188, 235, 21, 217, 64, 251, 181, 197, 161, 244, 175, 87, 197, 233, 232,
+ 15, 225, 99, 156, 156, 160, 80, 227, 175, 27, 57, 193, 67, 198, 62, 66, 156,
+ 188, 235, 21, 217, 64, 251, 181, 197, 161, 244, 175, 87, 197, 233,
+ ]),
+ 'KeyAgg list': Uint8Array.from([
+ 72, 28, 151, 28, 60, 11, 70, 215, 240, 178, 117, 174, 89, 141, 78, 44, 126,
+ 215, 49, 156, 89, 74, 92, 110, 199, 158, 160, 212, 153, 2, 148, 240, 72, 28,
+ 151, 28, 60, 11, 70, 215, 240, 178, 117, 174, 89, 141, 78, 44, 126, 215, 49,
+ 156, 89, 74, 92, 110, 199, 158, 160, 212, 153, 2, 148, 240,
+ ]),
+ 'KeyAgg coefficient': Uint8Array.from([
+ 191, 201, 4, 3, 77, 28, 136, 232, 200, 14, 34, 229, 61, 36, 86, 109, 100,
+ 130, 78, 214, 66, 114, 129, 192, 145, 0, 249, 77, 205, 82, 201, 129, 191,
+ 201, 4, 3, 77, 28, 136, 232, 200, 14, 34, 229, 61, 36, 86, 109, 100, 130,
+ 78, 214, 66, 114, 129, 192, 145, 0, 249, 77, 205, 82, 201, 129,
+ ]),
+};
+/**
+ * Computes a tagged hash using the specified prefix and data.
+ *
+ * @param prefix - The prefix to use for the tagged hash. This should be one of the values from the `TaggedHashPrefix` enum.
+ * @param data - The data to hash, provided as a `Uint8Array`.
+ * @returns The resulting tagged hash as a `Uint8Array`.
+ */
+function taggedHash(prefix, data) {
+ return (0, sha256_1.sha256)(
+ tools.concat([exports.TAGGED_HASH_PREFIXES[prefix], data]),
+ );
+}
diff --git a/src/cjs/crypto.d.ts b/src/cjs/crypto.d.ts
new file mode 100644
index 000000000..0637e3b84
--- /dev/null
+++ b/src/cjs/crypto.d.ts
@@ -0,0 +1,55 @@
+/**
+ * A module for hashing functions.
+ * include ripemd160、sha1、sha256、hash160、hash256、taggedHash
+ *
+ * @packageDocumentation
+ */
+import { ripemd160 } from '@noble/hashes/ripemd160';
+import { sha256 } from '@noble/hashes/sha256';
+export { ripemd160, sha256 };
+export { sha1 } from '@noble/hashes/sha1';
+/**
+ * Computes the HASH160 (RIPEMD-160 after SHA-256) of the given buffer.
+ *
+ * @param buffer - The input data to be hashed.
+ * @returns The HASH160 of the input buffer.
+ */
+export declare function hash160(buffer: Uint8Array): Uint8Array;
+/**
+ * Computes the double SHA-256 hash of the given buffer.
+ *
+ * @param buffer - The input data to be hashed.
+ * @returns The double SHA-256 hash of the input buffer.
+ */
+export declare function hash256(buffer: Uint8Array): Uint8Array;
+export declare const TAGS: readonly ["BIP0340/challenge", "BIP0340/aux", "BIP0340/nonce", "TapLeaf", "TapBranch", "TapSighash", "TapTweak", "KeyAgg list", "KeyAgg coefficient"];
+export type TaggedHashPrefix = (typeof TAGS)[number];
+type TaggedHashPrefixes = {
+ [key in TaggedHashPrefix]: Uint8Array;
+};
+/**
+ * A collection of tagged hash prefixes used in various BIP (Bitcoin Improvement Proposals)
+ * and Taproot-related operations. Each prefix is represented as a `Uint8Array`.
+ *
+ * @constant
+ * @type {TaggedHashPrefixes}
+ *
+ * @property {'BIP0340/challenge'} - Prefix for BIP0340 challenge.
+ * @property {'BIP0340/aux'} - Prefix for BIP0340 auxiliary data.
+ * @property {'BIP0340/nonce'} - Prefix for BIP0340 nonce.
+ * @property {TapLeaf} - Prefix for Taproot leaf.
+ * @property {TapBranch} - Prefix for Taproot branch.
+ * @property {TapSighash} - Prefix for Taproot sighash.
+ * @property {TapTweak} - Prefix for Taproot tweak.
+ * @property {'KeyAgg list'} - Prefix for key aggregation list.
+ * @property {'KeyAgg coefficient'} - Prefix for key aggregation coefficient.
+ */
+export declare const TAGGED_HASH_PREFIXES: TaggedHashPrefixes;
+/**
+ * Computes a tagged hash using the specified prefix and data.
+ *
+ * @param prefix - The prefix to use for the tagged hash. This should be one of the values from the `TaggedHashPrefix` enum.
+ * @param data - The data to hash, provided as a `Uint8Array`.
+ * @returns The resulting tagged hash as a `Uint8Array`.
+ */
+export declare function taggedHash(prefix: TaggedHashPrefix, data: Uint8Array): Uint8Array;
diff --git a/src/cjs/ecc_lib.cjs b/src/cjs/ecc_lib.cjs
new file mode 100644
index 000000000..707d345d1
--- /dev/null
+++ b/src/cjs/ecc_lib.cjs
@@ -0,0 +1,156 @@
+'use strict';
+var __createBinding =
+ (this && this.__createBinding) ||
+ (Object.create
+ ? function (o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ var desc = Object.getOwnPropertyDescriptor(m, k);
+ if (
+ !desc ||
+ ('get' in desc ? !m.__esModule : desc.writable || desc.configurable)
+ ) {
+ desc = {
+ enumerable: true,
+ get: function () {
+ return m[k];
+ },
+ };
+ }
+ Object.defineProperty(o, k2, desc);
+ }
+ : function (o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ o[k2] = m[k];
+ });
+var __setModuleDefault =
+ (this && this.__setModuleDefault) ||
+ (Object.create
+ ? function (o, v) {
+ Object.defineProperty(o, 'default', { enumerable: true, value: v });
+ }
+ : function (o, v) {
+ o['default'] = v;
+ });
+var __importStar =
+ (this && this.__importStar) ||
+ function (mod) {
+ if (mod && mod.__esModule) return mod;
+ var result = {};
+ if (mod != null)
+ for (var k in mod)
+ if (k !== 'default' && Object.prototype.hasOwnProperty.call(mod, k))
+ __createBinding(result, mod, k);
+ __setModuleDefault(result, mod);
+ return result;
+ };
+Object.defineProperty(exports, '__esModule', { value: true });
+exports.initEccLib = initEccLib;
+exports.getEccLib = getEccLib;
+const tools = __importStar(require('uint8array-tools'));
+const _ECCLIB_CACHE = {};
+/**
+ * Initializes the ECC library with the provided instance.
+ * If `eccLib` is `undefined`, the library will be cleared.
+ * If `eccLib` is a new instance, it will be verified before setting it as the active library.
+ *
+ * @param eccLib The instance of the ECC library to initialize.
+ * @param opts Extra initialization options. Use {DANGER_DO_NOT_VERIFY_ECCLIB:true} if ecc verification should not be executed. Not recommended!
+ */
+function initEccLib(eccLib, opts) {
+ if (!eccLib) {
+ // allow clearing the library
+ _ECCLIB_CACHE.eccLib = eccLib;
+ } else if (eccLib !== _ECCLIB_CACHE.eccLib) {
+ if (!opts?.DANGER_DO_NOT_VERIFY_ECCLIB)
+ // new instance, verify it
+ verifyEcc(eccLib);
+ _ECCLIB_CACHE.eccLib = eccLib;
+ }
+}
+/**
+ * Retrieves the ECC Library instance.
+ * Throws an error if the ECC Library is not provided.
+ * You must call initEccLib() with a valid TinySecp256k1Interface instance before calling this function.
+ * @returns The ECC Library instance.
+ * @throws Error if the ECC Library is not provided.
+ */
+function getEccLib() {
+ if (!_ECCLIB_CACHE.eccLib)
+ throw new Error(
+ 'No ECC Library provided. You must call initEccLib() with a valid TinySecp256k1Interface instance',
+ );
+ return _ECCLIB_CACHE.eccLib;
+}
+const h = hex => tools.fromHex(hex);
+/**
+ * Verifies the ECC functionality.
+ *
+ * @param ecc - The TinySecp256k1Interface object.
+ */
+function verifyEcc(ecc) {
+ assert(typeof ecc.isXOnlyPoint === 'function');
+ assert(
+ ecc.isXOnlyPoint(
+ h('79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798'),
+ ),
+ );
+ assert(
+ ecc.isXOnlyPoint(
+ h('fffffffffffffffffffffffffffffffffffffffffffffffffffffffeeffffc2e'),
+ ),
+ );
+ assert(
+ ecc.isXOnlyPoint(
+ h('f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9'),
+ ),
+ );
+ assert(
+ ecc.isXOnlyPoint(
+ h('0000000000000000000000000000000000000000000000000000000000000001'),
+ ),
+ );
+ assert(
+ !ecc.isXOnlyPoint(
+ h('0000000000000000000000000000000000000000000000000000000000000000'),
+ ),
+ );
+ assert(
+ !ecc.isXOnlyPoint(
+ h('fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f'),
+ ),
+ );
+ assert(typeof ecc.xOnlyPointAddTweak === 'function');
+ tweakAddVectors.forEach(t => {
+ const r = ecc.xOnlyPointAddTweak(h(t.pubkey), h(t.tweak));
+ if (t.result === null) {
+ assert(r === null);
+ } else {
+ assert(r !== null);
+ assert(r.parity === t.parity);
+ assert(tools.compare(r.xOnlyPubkey, h(t.result)) === 0);
+ }
+ });
+}
+function assert(bool) {
+ if (!bool) throw new Error('ecc library invalid');
+}
+const tweakAddVectors = [
+ {
+ pubkey: '79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798',
+ tweak: 'fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140',
+ parity: -1,
+ result: null,
+ },
+ {
+ pubkey: '1617d38ed8d8657da4d4761e8057bc396ea9e4b9d29776d4be096016dbd2509b',
+ tweak: 'a8397a935f0dfceba6ba9618f6451ef4d80637abf4e6af2669fbc9de6a8fd2ac',
+ parity: 1,
+ result: 'e478f99dab91052ab39a33ea35fd5e6e4933f4d28023cd597c9a1f6760346adf',
+ },
+ {
+ pubkey: '2c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e668680991',
+ tweak: '823c3cd2142744b075a87eade7e1b8678ba308d566226a0056ca2b7a76f86b47',
+ parity: 0,
+ result: '9534f8dc8c6deda2dc007655981c78b49c5d96c778fbf363462a11ec9dfd948c',
+ },
+];
diff --git a/src/cjs/ecc_lib.d.ts b/src/cjs/ecc_lib.d.ts
new file mode 100644
index 000000000..46fa113dc
--- /dev/null
+++ b/src/cjs/ecc_lib.d.ts
@@ -0,0 +1,20 @@
+import { TinySecp256k1Interface } from './types.js';
+/**
+ * Initializes the ECC library with the provided instance.
+ * If `eccLib` is `undefined`, the library will be cleared.
+ * If `eccLib` is a new instance, it will be verified before setting it as the active library.
+ *
+ * @param eccLib The instance of the ECC library to initialize.
+ * @param opts Extra initialization options. Use {DANGER_DO_NOT_VERIFY_ECCLIB:true} if ecc verification should not be executed. Not recommended!
+ */
+export declare function initEccLib(eccLib: TinySecp256k1Interface | undefined, opts?: {
+ DANGER_DO_NOT_VERIFY_ECCLIB: boolean;
+}): void;
+/**
+ * Retrieves the ECC Library instance.
+ * Throws an error if the ECC Library is not provided.
+ * You must call initEccLib() with a valid TinySecp256k1Interface instance before calling this function.
+ * @returns The ECC Library instance.
+ * @throws Error if the ECC Library is not provided.
+ */
+export declare function getEccLib(): TinySecp256k1Interface;
diff --git a/src/cjs/index.cjs b/src/cjs/index.cjs
new file mode 100644
index 000000000..8bd22c864
--- /dev/null
+++ b/src/cjs/index.cjs
@@ -0,0 +1,110 @@
+'use strict';
+var __createBinding =
+ (this && this.__createBinding) ||
+ (Object.create
+ ? function (o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ var desc = Object.getOwnPropertyDescriptor(m, k);
+ if (
+ !desc ||
+ ('get' in desc ? !m.__esModule : desc.writable || desc.configurable)
+ ) {
+ desc = {
+ enumerable: true,
+ get: function () {
+ return m[k];
+ },
+ };
+ }
+ Object.defineProperty(o, k2, desc);
+ }
+ : function (o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ o[k2] = m[k];
+ });
+var __setModuleDefault =
+ (this && this.__setModuleDefault) ||
+ (Object.create
+ ? function (o, v) {
+ Object.defineProperty(o, 'default', { enumerable: true, value: v });
+ }
+ : function (o, v) {
+ o['default'] = v;
+ });
+var __importStar =
+ (this && this.__importStar) ||
+ function (mod) {
+ if (mod && mod.__esModule) return mod;
+ var result = {};
+ if (mod != null)
+ for (var k in mod)
+ if (k !== 'default' && Object.prototype.hasOwnProperty.call(mod, k))
+ __createBinding(result, mod, k);
+ __setModuleDefault(result, mod);
+ return result;
+ };
+Object.defineProperty(exports, '__esModule', { value: true });
+exports.initEccLib =
+ exports.Transaction =
+ exports.opcodes =
+ exports.toXOnly =
+ exports.Psbt =
+ exports.Block =
+ exports.script =
+ exports.payments =
+ exports.networks =
+ exports.crypto =
+ exports.address =
+ void 0;
+const address = __importStar(require('./address.cjs'));
+exports.address = address;
+const crypto = __importStar(require('./crypto.cjs'));
+exports.crypto = crypto;
+const networks = __importStar(require('./networks.cjs'));
+exports.networks = networks;
+const payments = __importStar(require('./payments/index.cjs'));
+exports.payments = payments;
+const script = __importStar(require('./script.cjs'));
+exports.script = script;
+var block_js_1 = require('./block.cjs');
+Object.defineProperty(exports, 'Block', {
+ enumerable: true,
+ get: function () {
+ return block_js_1.Block;
+ },
+});
+var psbt_js_1 = require('./psbt.cjs');
+Object.defineProperty(exports, 'Psbt', {
+ enumerable: true,
+ get: function () {
+ return psbt_js_1.Psbt;
+ },
+});
+Object.defineProperty(exports, 'toXOnly', {
+ enumerable: true,
+ get: function () {
+ return psbt_js_1.toXOnly;
+ },
+});
+/** @hidden */
+var ops_js_1 = require('./ops.cjs');
+Object.defineProperty(exports, 'opcodes', {
+ enumerable: true,
+ get: function () {
+ return ops_js_1.OPS;
+ },
+});
+var transaction_js_1 = require('./transaction.cjs');
+Object.defineProperty(exports, 'Transaction', {
+ enumerable: true,
+ get: function () {
+ return transaction_js_1.Transaction;
+ },
+});
+var ecc_lib_js_1 = require('./ecc_lib.cjs');
+Object.defineProperty(exports, 'initEccLib', {
+ enumerable: true,
+ get: function () {
+ return ecc_lib_js_1.initEccLib;
+ },
+});
diff --git a/src/cjs/index.d.ts b/src/cjs/index.d.ts
new file mode 100644
index 000000000..f2badff81
--- /dev/null
+++ b/src/cjs/index.d.ts
@@ -0,0 +1,19 @@
+import * as address from './address.js';
+import * as crypto from './crypto.js';
+import * as networks from './networks.js';
+import * as payments from './payments/index.js';
+import * as script from './script.js';
+export { address, crypto, networks, payments, script };
+export { Block } from './block.js';
+/** @hidden */
+export { TaggedHashPrefix } from './crypto.js';
+export { Psbt, PsbtTxInput, PsbtTxOutput, Signer, SignerAsync, HDSigner, HDSignerAsync, toXOnly, } from './psbt.js';
+/** @hidden */
+export { OPS as opcodes } from './ops.js';
+export { Transaction } from './transaction.js';
+/** @hidden */
+export { Network } from './networks.js';
+/** @hidden */
+export { Payment, PaymentCreator, PaymentOpts, Stack, StackElement, } from './payments/index.js';
+export { Input as TxInput, Output as TxOutput } from './transaction.js';
+export { initEccLib } from './ecc_lib.js';
diff --git a/src/cjs/merkle.cjs b/src/cjs/merkle.cjs
new file mode 100644
index 000000000..a94e9721e
--- /dev/null
+++ b/src/cjs/merkle.cjs
@@ -0,0 +1,74 @@
+'use strict';
+var __createBinding =
+ (this && this.__createBinding) ||
+ (Object.create
+ ? function (o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ var desc = Object.getOwnPropertyDescriptor(m, k);
+ if (
+ !desc ||
+ ('get' in desc ? !m.__esModule : desc.writable || desc.configurable)
+ ) {
+ desc = {
+ enumerable: true,
+ get: function () {
+ return m[k];
+ },
+ };
+ }
+ Object.defineProperty(o, k2, desc);
+ }
+ : function (o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ o[k2] = m[k];
+ });
+var __setModuleDefault =
+ (this && this.__setModuleDefault) ||
+ (Object.create
+ ? function (o, v) {
+ Object.defineProperty(o, 'default', { enumerable: true, value: v });
+ }
+ : function (o, v) {
+ o['default'] = v;
+ });
+var __importStar =
+ (this && this.__importStar) ||
+ function (mod) {
+ if (mod && mod.__esModule) return mod;
+ var result = {};
+ if (mod != null)
+ for (var k in mod)
+ if (k !== 'default' && Object.prototype.hasOwnProperty.call(mod, k))
+ __createBinding(result, mod, k);
+ __setModuleDefault(result, mod);
+ return result;
+ };
+Object.defineProperty(exports, '__esModule', { value: true });
+exports.fastMerkleRoot = fastMerkleRoot;
+const tools = __importStar(require('uint8array-tools'));
+/**
+ * Calculates the Merkle root of an array of buffers using a specified digest function.
+ *
+ * @param values - The array of buffers.
+ * @param digestFn - The digest function used to calculate the hash of the concatenated buffers.
+ * @returns The Merkle root as a buffer.
+ * @throws {TypeError} If the values parameter is not an array or the digestFn parameter is not a function.
+ */
+function fastMerkleRoot(values, digestFn) {
+ if (!Array.isArray(values)) throw TypeError('Expected values Array');
+ if (typeof digestFn !== 'function')
+ throw TypeError('Expected digest Function');
+ let length = values.length;
+ const results = values.concat();
+ while (length > 1) {
+ let j = 0;
+ for (let i = 0; i < length; i += 2, ++j) {
+ const left = results[i];
+ const right = i + 1 === length ? left : results[i + 1];
+ const data = tools.concat([left, right]);
+ results[j] = digestFn(data);
+ }
+ length = j;
+ }
+ return results[0];
+}
diff --git a/src/cjs/merkle.d.ts b/src/cjs/merkle.d.ts
new file mode 100644
index 000000000..b98fe066a
--- /dev/null
+++ b/src/cjs/merkle.d.ts
@@ -0,0 +1,9 @@
+/**
+ * Calculates the Merkle root of an array of buffers using a specified digest function.
+ *
+ * @param values - The array of buffers.
+ * @param digestFn - The digest function used to calculate the hash of the concatenated buffers.
+ * @returns The Merkle root as a buffer.
+ * @throws {TypeError} If the values parameter is not an array or the digestFn parameter is not a function.
+ */
+export declare function fastMerkleRoot(values: Uint8Array[], digestFn: (b: Uint8Array) => Uint8Array): Uint8Array;
diff --git a/src/networks.js b/src/cjs/networks.cjs
similarity index 51%
rename from src/networks.js
rename to src/cjs/networks.cjs
index ea710f8a2..26aa44e0d 100644
--- a/src/networks.js
+++ b/src/cjs/networks.cjs
@@ -1,17 +1,47 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
exports.testnet = exports.regtest = exports.bitcoin = void 0;
+/**
+ * Represents the Bitcoin network configuration.
+ */
exports.bitcoin = {
+ /**
+ * The message prefix used for signing Bitcoin messages.
+ */
messagePrefix: '\x18Bitcoin Signed Message:\n',
+ /**
+ * The Bech32 prefix used for Bitcoin addresses.
+ */
bech32: 'bc',
+ /**
+ * The BIP32 key prefixes for Bitcoin.
+ */
bip32: {
+ /**
+ * The public key prefix for BIP32 extended public keys.
+ */
public: 0x0488b21e,
+ /**
+ * The private key prefix for BIP32 extended private keys.
+ */
private: 0x0488ade4,
},
+ /**
+ * The prefix for Bitcoin public key hashes.
+ */
pubKeyHash: 0x00,
+ /**
+ * The prefix for Bitcoin script hashes.
+ */
scriptHash: 0x05,
+ /**
+ * The prefix for Bitcoin Wallet Import Format (WIF) private keys.
+ */
wif: 0x80,
};
+/**
+ * Represents the regtest network configuration.
+ */
exports.regtest = {
messagePrefix: '\x18Bitcoin Signed Message:\n',
bech32: 'bcrt',
@@ -23,6 +53,9 @@ exports.regtest = {
scriptHash: 0xc4,
wif: 0xef,
};
+/**
+ * Represents the testnet network configuration.
+ */
exports.testnet = {
messagePrefix: '\x18Bitcoin Signed Message:\n',
bech32: 'tb',
diff --git a/src/cjs/networks.d.ts b/src/cjs/networks.d.ts
new file mode 100644
index 000000000..f35f4b7b7
--- /dev/null
+++ b/src/cjs/networks.d.ts
@@ -0,0 +1,35 @@
+/**
+ * This module defines the network configurations for Bitcoin and its variants, including message prefixes,
+ * Bech32 address format, BIP32 key derivation prefixes, and other address-related configurations.
+ * It supports Bitcoin, Bitcoin testnet, and Bitcoin regtest networks.
+ *
+ * Additional information on address prefixes can be found here:
+ * - https://en.bitcoin.it/wiki/List_of_address_prefixes
+ *
+ * @packageDocumentation
+ */
+export interface Network {
+ messagePrefix: string;
+ bech32: string;
+ bip32: Bip32;
+ pubKeyHash: number;
+ scriptHash: number;
+ wif: number;
+}
+interface Bip32 {
+ public: number;
+ private: number;
+}
+/**
+ * Represents the Bitcoin network configuration.
+ */
+export declare const bitcoin: Network;
+/**
+ * Represents the regtest network configuration.
+ */
+export declare const regtest: Network;
+/**
+ * Represents the testnet network configuration.
+ */
+export declare const testnet: Network;
+export {};
diff --git a/src/cjs/ops.cjs b/src/cjs/ops.cjs
new file mode 100644
index 000000000..7e9f27bb8
--- /dev/null
+++ b/src/cjs/ops.cjs
@@ -0,0 +1,126 @@
+'use strict';
+Object.defineProperty(exports, '__esModule', { value: true });
+exports.OPS = void 0;
+// Define OPS enum
+var OPS;
+(function (OPS) {
+ OPS[(OPS['OP_FALSE'] = 0)] = 'OP_FALSE';
+ OPS[(OPS['OP_0'] = 0)] = 'OP_0';
+ OPS[(OPS['OP_PUSHDATA1'] = 76)] = 'OP_PUSHDATA1';
+ OPS[(OPS['OP_PUSHDATA2'] = 77)] = 'OP_PUSHDATA2';
+ OPS[(OPS['OP_PUSHDATA4'] = 78)] = 'OP_PUSHDATA4';
+ OPS[(OPS['OP_1NEGATE'] = 79)] = 'OP_1NEGATE';
+ OPS[(OPS['OP_RESERVED'] = 80)] = 'OP_RESERVED';
+ OPS[(OPS['OP_TRUE'] = 81)] = 'OP_TRUE';
+ OPS[(OPS['OP_1'] = 81)] = 'OP_1';
+ OPS[(OPS['OP_2'] = 82)] = 'OP_2';
+ OPS[(OPS['OP_3'] = 83)] = 'OP_3';
+ OPS[(OPS['OP_4'] = 84)] = 'OP_4';
+ OPS[(OPS['OP_5'] = 85)] = 'OP_5';
+ OPS[(OPS['OP_6'] = 86)] = 'OP_6';
+ OPS[(OPS['OP_7'] = 87)] = 'OP_7';
+ OPS[(OPS['OP_8'] = 88)] = 'OP_8';
+ OPS[(OPS['OP_9'] = 89)] = 'OP_9';
+ OPS[(OPS['OP_10'] = 90)] = 'OP_10';
+ OPS[(OPS['OP_11'] = 91)] = 'OP_11';
+ OPS[(OPS['OP_12'] = 92)] = 'OP_12';
+ OPS[(OPS['OP_13'] = 93)] = 'OP_13';
+ OPS[(OPS['OP_14'] = 94)] = 'OP_14';
+ OPS[(OPS['OP_15'] = 95)] = 'OP_15';
+ OPS[(OPS['OP_16'] = 96)] = 'OP_16';
+ OPS[(OPS['OP_NOP'] = 97)] = 'OP_NOP';
+ OPS[(OPS['OP_VER'] = 98)] = 'OP_VER';
+ OPS[(OPS['OP_IF'] = 99)] = 'OP_IF';
+ OPS[(OPS['OP_NOTIF'] = 100)] = 'OP_NOTIF';
+ OPS[(OPS['OP_VERIF'] = 101)] = 'OP_VERIF';
+ OPS[(OPS['OP_VERNOTIF'] = 102)] = 'OP_VERNOTIF';
+ OPS[(OPS['OP_ELSE'] = 103)] = 'OP_ELSE';
+ OPS[(OPS['OP_ENDIF'] = 104)] = 'OP_ENDIF';
+ OPS[(OPS['OP_VERIFY'] = 105)] = 'OP_VERIFY';
+ OPS[(OPS['OP_RETURN'] = 106)] = 'OP_RETURN';
+ OPS[(OPS['OP_TOALTSTACK'] = 107)] = 'OP_TOALTSTACK';
+ OPS[(OPS['OP_FROMALTSTACK'] = 108)] = 'OP_FROMALTSTACK';
+ OPS[(OPS['OP_2DROP'] = 109)] = 'OP_2DROP';
+ OPS[(OPS['OP_2DUP'] = 110)] = 'OP_2DUP';
+ OPS[(OPS['OP_3DUP'] = 111)] = 'OP_3DUP';
+ OPS[(OPS['OP_2OVER'] = 112)] = 'OP_2OVER';
+ OPS[(OPS['OP_2ROT'] = 113)] = 'OP_2ROT';
+ OPS[(OPS['OP_2SWAP'] = 114)] = 'OP_2SWAP';
+ OPS[(OPS['OP_IFDUP'] = 115)] = 'OP_IFDUP';
+ OPS[(OPS['OP_DEPTH'] = 116)] = 'OP_DEPTH';
+ OPS[(OPS['OP_DROP'] = 117)] = 'OP_DROP';
+ OPS[(OPS['OP_DUP'] = 118)] = 'OP_DUP';
+ OPS[(OPS['OP_NIP'] = 119)] = 'OP_NIP';
+ OPS[(OPS['OP_OVER'] = 120)] = 'OP_OVER';
+ OPS[(OPS['OP_PICK'] = 121)] = 'OP_PICK';
+ OPS[(OPS['OP_ROLL'] = 122)] = 'OP_ROLL';
+ OPS[(OPS['OP_ROT'] = 123)] = 'OP_ROT';
+ OPS[(OPS['OP_SWAP'] = 124)] = 'OP_SWAP';
+ OPS[(OPS['OP_TUCK'] = 125)] = 'OP_TUCK';
+ OPS[(OPS['OP_CAT'] = 126)] = 'OP_CAT';
+ OPS[(OPS['OP_SUBSTR'] = 127)] = 'OP_SUBSTR';
+ OPS[(OPS['OP_LEFT'] = 128)] = 'OP_LEFT';
+ OPS[(OPS['OP_RIGHT'] = 129)] = 'OP_RIGHT';
+ OPS[(OPS['OP_SIZE'] = 130)] = 'OP_SIZE';
+ OPS[(OPS['OP_INVERT'] = 131)] = 'OP_INVERT';
+ OPS[(OPS['OP_AND'] = 132)] = 'OP_AND';
+ OPS[(OPS['OP_OR'] = 133)] = 'OP_OR';
+ OPS[(OPS['OP_XOR'] = 134)] = 'OP_XOR';
+ OPS[(OPS['OP_EQUAL'] = 135)] = 'OP_EQUAL';
+ OPS[(OPS['OP_EQUALVERIFY'] = 136)] = 'OP_EQUALVERIFY';
+ OPS[(OPS['OP_RESERVED1'] = 137)] = 'OP_RESERVED1';
+ OPS[(OPS['OP_RESERVED2'] = 138)] = 'OP_RESERVED2';
+ OPS[(OPS['OP_1ADD'] = 139)] = 'OP_1ADD';
+ OPS[(OPS['OP_1SUB'] = 140)] = 'OP_1SUB';
+ OPS[(OPS['OP_2MUL'] = 141)] = 'OP_2MUL';
+ OPS[(OPS['OP_2DIV'] = 142)] = 'OP_2DIV';
+ OPS[(OPS['OP_NEGATE'] = 143)] = 'OP_NEGATE';
+ OPS[(OPS['OP_ABS'] = 144)] = 'OP_ABS';
+ OPS[(OPS['OP_NOT'] = 145)] = 'OP_NOT';
+ OPS[(OPS['OP_0NOTEQUAL'] = 146)] = 'OP_0NOTEQUAL';
+ OPS[(OPS['OP_ADD'] = 147)] = 'OP_ADD';
+ OPS[(OPS['OP_SUB'] = 148)] = 'OP_SUB';
+ OPS[(OPS['OP_MUL'] = 149)] = 'OP_MUL';
+ OPS[(OPS['OP_DIV'] = 150)] = 'OP_DIV';
+ OPS[(OPS['OP_MOD'] = 151)] = 'OP_MOD';
+ OPS[(OPS['OP_LSHIFT'] = 152)] = 'OP_LSHIFT';
+ OPS[(OPS['OP_RSHIFT'] = 153)] = 'OP_RSHIFT';
+ OPS[(OPS['OP_BOOLAND'] = 154)] = 'OP_BOOLAND';
+ OPS[(OPS['OP_BOOLOR'] = 155)] = 'OP_BOOLOR';
+ OPS[(OPS['OP_NUMEQUAL'] = 156)] = 'OP_NUMEQUAL';
+ OPS[(OPS['OP_NUMEQUALVERIFY'] = 157)] = 'OP_NUMEQUALVERIFY';
+ OPS[(OPS['OP_NUMNOTEQUAL'] = 158)] = 'OP_NUMNOTEQUAL';
+ OPS[(OPS['OP_LESSTHAN'] = 159)] = 'OP_LESSTHAN';
+ OPS[(OPS['OP_GREATERTHAN'] = 160)] = 'OP_GREATERTHAN';
+ OPS[(OPS['OP_LESSTHANOREQUAL'] = 161)] = 'OP_LESSTHANOREQUAL';
+ OPS[(OPS['OP_GREATERTHANOREQUAL'] = 162)] = 'OP_GREATERTHANOREQUAL';
+ OPS[(OPS['OP_MIN'] = 163)] = 'OP_MIN';
+ OPS[(OPS['OP_MAX'] = 164)] = 'OP_MAX';
+ OPS[(OPS['OP_WITHIN'] = 165)] = 'OP_WITHIN';
+ OPS[(OPS['OP_RIPEMD160'] = 166)] = 'OP_RIPEMD160';
+ OPS[(OPS['OP_SHA1'] = 167)] = 'OP_SHA1';
+ OPS[(OPS['OP_SHA256'] = 168)] = 'OP_SHA256';
+ OPS[(OPS['OP_HASH160'] = 169)] = 'OP_HASH160';
+ OPS[(OPS['OP_HASH256'] = 170)] = 'OP_HASH256';
+ OPS[(OPS['OP_CODESEPARATOR'] = 171)] = 'OP_CODESEPARATOR';
+ OPS[(OPS['OP_CHECKSIG'] = 172)] = 'OP_CHECKSIG';
+ OPS[(OPS['OP_CHECKSIGVERIFY'] = 173)] = 'OP_CHECKSIGVERIFY';
+ OPS[(OPS['OP_CHECKMULTISIG'] = 174)] = 'OP_CHECKMULTISIG';
+ OPS[(OPS['OP_CHECKMULTISIGVERIFY'] = 175)] = 'OP_CHECKMULTISIGVERIFY';
+ OPS[(OPS['OP_NOP1'] = 176)] = 'OP_NOP1';
+ OPS[(OPS['OP_CHECKLOCKTIMEVERIFY'] = 177)] = 'OP_CHECKLOCKTIMEVERIFY';
+ OPS[(OPS['OP_NOP2'] = 177)] = 'OP_NOP2';
+ OPS[(OPS['OP_CHECKSEQUENCEVERIFY'] = 178)] = 'OP_CHECKSEQUENCEVERIFY';
+ OPS[(OPS['OP_NOP3'] = 178)] = 'OP_NOP3';
+ OPS[(OPS['OP_NOP4'] = 179)] = 'OP_NOP4';
+ OPS[(OPS['OP_NOP5'] = 180)] = 'OP_NOP5';
+ OPS[(OPS['OP_NOP6'] = 181)] = 'OP_NOP6';
+ OPS[(OPS['OP_NOP7'] = 182)] = 'OP_NOP7';
+ OPS[(OPS['OP_NOP8'] = 183)] = 'OP_NOP8';
+ OPS[(OPS['OP_NOP9'] = 184)] = 'OP_NOP9';
+ OPS[(OPS['OP_NOP10'] = 185)] = 'OP_NOP10';
+ OPS[(OPS['OP_CHECKSIGADD'] = 186)] = 'OP_CHECKSIGADD';
+ OPS[(OPS['OP_PUBKEYHASH'] = 253)] = 'OP_PUBKEYHASH';
+ OPS[(OPS['OP_PUBKEY'] = 254)] = 'OP_PUBKEY';
+ OPS[(OPS['OP_INVALIDOPCODE'] = 255)] = 'OP_INVALIDOPCODE';
+})(OPS || (exports.OPS = OPS = {}));
diff --git a/src/cjs/ops.d.ts b/src/cjs/ops.d.ts
new file mode 100644
index 000000000..e3dd4b27e
--- /dev/null
+++ b/src/cjs/ops.d.ts
@@ -0,0 +1,122 @@
+declare enum OPS {
+ OP_FALSE = 0,
+ OP_0 = 0,// Avoid duplicate value
+ OP_PUSHDATA1 = 76,
+ OP_PUSHDATA2 = 77,
+ OP_PUSHDATA4 = 78,
+ OP_1NEGATE = 79,
+ OP_RESERVED = 80,
+ OP_TRUE = 81,
+ OP_1 = 81,// Avoid duplicate value
+ OP_2 = 82,
+ OP_3 = 83,
+ OP_4 = 84,
+ OP_5 = 85,
+ OP_6 = 86,
+ OP_7 = 87,
+ OP_8 = 88,
+ OP_9 = 89,
+ OP_10 = 90,
+ OP_11 = 91,
+ OP_12 = 92,
+ OP_13 = 93,
+ OP_14 = 94,
+ OP_15 = 95,
+ OP_16 = 96,
+ OP_NOP = 97,
+ OP_VER = 98,
+ OP_IF = 99,
+ OP_NOTIF = 100,
+ OP_VERIF = 101,
+ OP_VERNOTIF = 102,
+ OP_ELSE = 103,
+ OP_ENDIF = 104,
+ OP_VERIFY = 105,
+ OP_RETURN = 106,
+ OP_TOALTSTACK = 107,
+ OP_FROMALTSTACK = 108,
+ OP_2DROP = 109,
+ OP_2DUP = 110,
+ OP_3DUP = 111,
+ OP_2OVER = 112,
+ OP_2ROT = 113,
+ OP_2SWAP = 114,
+ OP_IFDUP = 115,
+ OP_DEPTH = 116,
+ OP_DROP = 117,
+ OP_DUP = 118,
+ OP_NIP = 119,
+ OP_OVER = 120,
+ OP_PICK = 121,
+ OP_ROLL = 122,
+ OP_ROT = 123,
+ OP_SWAP = 124,
+ OP_TUCK = 125,
+ OP_CAT = 126,
+ OP_SUBSTR = 127,
+ OP_LEFT = 128,
+ OP_RIGHT = 129,
+ OP_SIZE = 130,
+ OP_INVERT = 131,
+ OP_AND = 132,
+ OP_OR = 133,
+ OP_XOR = 134,
+ OP_EQUAL = 135,
+ OP_EQUALVERIFY = 136,
+ OP_RESERVED1 = 137,
+ OP_RESERVED2 = 138,
+ OP_1ADD = 139,
+ OP_1SUB = 140,
+ OP_2MUL = 141,
+ OP_2DIV = 142,
+ OP_NEGATE = 143,
+ OP_ABS = 144,
+ OP_NOT = 145,
+ OP_0NOTEQUAL = 146,
+ OP_ADD = 147,
+ OP_SUB = 148,
+ OP_MUL = 149,
+ OP_DIV = 150,
+ OP_MOD = 151,
+ OP_LSHIFT = 152,
+ OP_RSHIFT = 153,
+ OP_BOOLAND = 154,
+ OP_BOOLOR = 155,
+ OP_NUMEQUAL = 156,
+ OP_NUMEQUALVERIFY = 157,
+ OP_NUMNOTEQUAL = 158,
+ OP_LESSTHAN = 159,
+ OP_GREATERTHAN = 160,
+ OP_LESSTHANOREQUAL = 161,
+ OP_GREATERTHANOREQUAL = 162,
+ OP_MIN = 163,
+ OP_MAX = 164,
+ OP_WITHIN = 165,
+ OP_RIPEMD160 = 166,
+ OP_SHA1 = 167,
+ OP_SHA256 = 168,
+ OP_HASH160 = 169,
+ OP_HASH256 = 170,
+ OP_CODESEPARATOR = 171,
+ OP_CHECKSIG = 172,
+ OP_CHECKSIGVERIFY = 173,
+ OP_CHECKMULTISIG = 174,
+ OP_CHECKMULTISIGVERIFY = 175,
+ OP_NOP1 = 176,
+ OP_CHECKLOCKTIMEVERIFY = 177,// Alias: OP_NOP2
+ OP_NOP2 = 177,
+ OP_CHECKSEQUENCEVERIFY = 178,// Alias: OP_NOP3
+ OP_NOP3 = 178,
+ OP_NOP4 = 179,
+ OP_NOP5 = 180,
+ OP_NOP6 = 181,
+ OP_NOP7 = 182,
+ OP_NOP8 = 183,
+ OP_NOP9 = 184,
+ OP_NOP10 = 185,
+ OP_CHECKSIGADD = 186,
+ OP_PUBKEYHASH = 253,
+ OP_PUBKEY = 254,
+ OP_INVALIDOPCODE = 255
+}
+export { OPS };
diff --git a/src/cjs/payments/bip341.cjs b/src/cjs/payments/bip341.cjs
new file mode 100644
index 000000000..c2adb3458
--- /dev/null
+++ b/src/cjs/payments/bip341.cjs
@@ -0,0 +1,192 @@
+'use strict';
+var __createBinding =
+ (this && this.__createBinding) ||
+ (Object.create
+ ? function (o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ var desc = Object.getOwnPropertyDescriptor(m, k);
+ if (
+ !desc ||
+ ('get' in desc ? !m.__esModule : desc.writable || desc.configurable)
+ ) {
+ desc = {
+ enumerable: true,
+ get: function () {
+ return m[k];
+ },
+ };
+ }
+ Object.defineProperty(o, k2, desc);
+ }
+ : function (o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ o[k2] = m[k];
+ });
+var __setModuleDefault =
+ (this && this.__setModuleDefault) ||
+ (Object.create
+ ? function (o, v) {
+ Object.defineProperty(o, 'default', { enumerable: true, value: v });
+ }
+ : function (o, v) {
+ o['default'] = v;
+ });
+var __importStar =
+ (this && this.__importStar) ||
+ function (mod) {
+ if (mod && mod.__esModule) return mod;
+ var result = {};
+ if (mod != null)
+ for (var k in mod)
+ if (k !== 'default' && Object.prototype.hasOwnProperty.call(mod, k))
+ __createBinding(result, mod, k);
+ __setModuleDefault(result, mod);
+ return result;
+ };
+Object.defineProperty(exports, '__esModule', { value: true });
+exports.MAX_TAPTREE_DEPTH = exports.LEAF_VERSION_TAPSCRIPT = void 0;
+exports.rootHashFromPath = rootHashFromPath;
+exports.toHashTree = toHashTree;
+exports.findScriptPath = findScriptPath;
+exports.tapleafHash = tapleafHash;
+exports.tapTweakHash = tapTweakHash;
+exports.tweakKey = tweakKey;
+const ecc_lib_js_1 = require('../ecc_lib.cjs');
+const bcrypto = __importStar(require('../crypto.cjs'));
+const bufferutils_js_1 = require('../bufferutils.cjs');
+const types_js_1 = require('../types.cjs');
+const tools = __importStar(require('uint8array-tools'));
+exports.LEAF_VERSION_TAPSCRIPT = 0xc0;
+exports.MAX_TAPTREE_DEPTH = 128;
+const isHashBranch = ht => 'left' in ht && 'right' in ht;
+/**
+ * Calculates the root hash from a given control block and leaf hash.
+ * @param controlBlock - The control block buffer.
+ * @param leafHash - The leaf hash buffer.
+ * @returns The root hash buffer.
+ * @throws {TypeError} If the control block length is less than 33.
+ */
+function rootHashFromPath(controlBlock, leafHash) {
+ if (controlBlock.length < 33)
+ throw new TypeError(
+ `The control-block length is too small. Got ${controlBlock.length}, expected min 33.`,
+ );
+ const m = (controlBlock.length - 33) / 32;
+ let kj = leafHash;
+ for (let j = 0; j < m; j++) {
+ const ej = controlBlock.slice(33 + 32 * j, 65 + 32 * j);
+ if (tools.compare(kj, ej) < 0) {
+ kj = tapBranchHash(kj, ej);
+ } else {
+ kj = tapBranchHash(ej, kj);
+ }
+ }
+ return kj;
+}
+/**
+ * Build a hash tree of merkle nodes from the scripts binary tree.
+ * @param scriptTree - the tree of scripts to pairwise hash.
+ */
+function toHashTree(scriptTree) {
+ if ((0, types_js_1.isTapleaf)(scriptTree))
+ return { hash: tapleafHash(scriptTree) };
+ const hashes = [toHashTree(scriptTree[0]), toHashTree(scriptTree[1])];
+ // hashes.sort((a, b) => a.hash.compare(b.hash));
+ hashes.sort((a, b) => tools.compare(a.hash, b.hash));
+ const [left, right] = hashes;
+ return {
+ hash: tapBranchHash(left.hash, right.hash),
+ left,
+ right,
+ };
+}
+/**
+ * Given a HashTree, finds the path from a particular hash to the root.
+ * @param node - the root of the tree
+ * @param hash - the hash to search for
+ * @returns - array of sibling hashes, from leaf (inclusive) to root
+ * (exclusive) needed to prove inclusion of the specified hash. undefined if no
+ * path is found
+ */
+function findScriptPath(node, hash) {
+ if (isHashBranch(node)) {
+ const leftPath = findScriptPath(node.left, hash);
+ if (leftPath !== undefined) return [...leftPath, node.right.hash];
+ const rightPath = findScriptPath(node.right, hash);
+ if (rightPath !== undefined) return [...rightPath, node.left.hash];
+ } else if (tools.compare(node.hash, hash) === 0) {
+ return [];
+ }
+ return undefined;
+}
+/**
+ * Calculates the tapleaf hash for a given Tapleaf object.
+ * @param leaf - The Tapleaf object to calculate the hash for.
+ * @returns The tapleaf hash as a Buffer.
+ */
+function tapleafHash(leaf) {
+ const version = leaf.version || exports.LEAF_VERSION_TAPSCRIPT;
+ return bcrypto.taggedHash(
+ 'TapLeaf',
+ tools.concat([Uint8Array.from([version]), serializeScript(leaf.output)]),
+ );
+}
+/**
+ * Computes the taproot tweak hash for a given public key and optional hash.
+ * If a hash is provided, the public key and hash are concatenated before computing the hash.
+ * If no hash is provided, only the public key is used to compute the hash.
+ *
+ * @param pubKey - The public key buffer.
+ * @param h - The optional hash buffer.
+ * @returns The taproot tweak hash.
+ */
+function tapTweakHash(pubKey, h) {
+ return bcrypto.taggedHash(
+ 'TapTweak',
+ tools.concat(h ? [pubKey, h] : [pubKey]),
+ );
+}
+/**
+ * Tweak a public key with a given tweak hash.
+ * @param pubKey - The public key to be tweaked.
+ * @param h - The tweak hash.
+ * @returns The tweaked public key or null if the input is invalid.
+ */
+function tweakKey(pubKey, h) {
+ if (!(pubKey instanceof Uint8Array)) return null;
+ if (pubKey.length !== 32) return null;
+ if (h && h.length !== 32) return null;
+ const tweakHash = tapTweakHash(pubKey, h);
+ const res = (0, ecc_lib_js_1.getEccLib)().xOnlyPointAddTweak(
+ pubKey,
+ tweakHash,
+ );
+ if (!res || res.xOnlyPubkey === null) return null;
+ return {
+ parity: res.parity,
+ x: Uint8Array.from(res.xOnlyPubkey),
+ };
+}
+/**
+ * Computes the TapBranch hash by concatenating two buffers and applying the 'TapBranch' tagged hash algorithm.
+ *
+ * @param a - The first buffer.
+ * @param b - The second buffer.
+ * @returns The TapBranch hash of the concatenated buffers.
+ */
+function tapBranchHash(a, b) {
+ return bcrypto.taggedHash('TapBranch', tools.concat([a, b]));
+}
+/**
+ * Serializes a script by encoding its length as a varint and concatenating it with the script.
+ *
+ * @param s - The script to be serialized.
+ * @returns The serialized script as a Buffer.
+ */
+function serializeScript(s) {
+ /* global BigInt */
+ const varintLen = bufferutils_js_1.varuint.encodingLength(s.length);
+ const buffer = new Uint8Array(varintLen);
+ bufferutils_js_1.varuint.encode(s.length, buffer);
+ return tools.concat([buffer, s]);
+}
diff --git a/src/cjs/payments/bip341.d.ts b/src/cjs/payments/bip341.d.ts
new file mode 100644
index 000000000..12b388578
--- /dev/null
+++ b/src/cjs/payments/bip341.d.ts
@@ -0,0 +1,68 @@
+import { Tapleaf, Taptree } from '../types.js';
+export declare const LEAF_VERSION_TAPSCRIPT = 192;
+export declare const MAX_TAPTREE_DEPTH = 128;
+interface HashLeaf {
+ hash: Uint8Array;
+}
+interface HashBranch {
+ hash: Uint8Array;
+ left: HashTree;
+ right: HashTree;
+}
+interface TweakedPublicKey {
+ parity: number;
+ x: Uint8Array;
+}
+/**
+ * Binary tree representing leaf, branch, and root node hashes of a Taptree.
+ * Each node contains a hash, and potentially left and right branch hashes.
+ * This tree is used for 2 purposes: Providing the root hash for tweaking,
+ * and calculating merkle inclusion proofs when constructing a control block.
+ */
+export type HashTree = HashLeaf | HashBranch;
+/**
+ * Calculates the root hash from a given control block and leaf hash.
+ * @param controlBlock - The control block buffer.
+ * @param leafHash - The leaf hash buffer.
+ * @returns The root hash buffer.
+ * @throws {TypeError} If the control block length is less than 33.
+ */
+export declare function rootHashFromPath(controlBlock: Uint8Array, leafHash: Uint8Array): Uint8Array;
+/**
+ * Build a hash tree of merkle nodes from the scripts binary tree.
+ * @param scriptTree - the tree of scripts to pairwise hash.
+ */
+export declare function toHashTree(scriptTree: Taptree): HashTree;
+/**
+ * Given a HashTree, finds the path from a particular hash to the root.
+ * @param node - the root of the tree
+ * @param hash - the hash to search for
+ * @returns - array of sibling hashes, from leaf (inclusive) to root
+ * (exclusive) needed to prove inclusion of the specified hash. undefined if no
+ * path is found
+ */
+export declare function findScriptPath(node: HashTree, hash: Uint8Array): Uint8Array[] | undefined;
+/**
+ * Calculates the tapleaf hash for a given Tapleaf object.
+ * @param leaf - The Tapleaf object to calculate the hash for.
+ * @returns The tapleaf hash as a Buffer.
+ */
+export declare function tapleafHash(leaf: Tapleaf): Uint8Array;
+/**
+ * Computes the taproot tweak hash for a given public key and optional hash.
+ * If a hash is provided, the public key and hash are concatenated before computing the hash.
+ * If no hash is provided, only the public key is used to compute the hash.
+ *
+ * @param pubKey - The public key buffer.
+ * @param h - The optional hash buffer.
+ * @returns The taproot tweak hash.
+ */
+export declare function tapTweakHash(pubKey: Uint8Array, h: Uint8Array | undefined): Uint8Array;
+/**
+ * Tweak a public key with a given tweak hash.
+ * @param pubKey - The public key to be tweaked.
+ * @param h - The tweak hash.
+ * @returns The tweaked public key or null if the input is invalid.
+ */
+export declare function tweakKey(pubKey: Uint8Array, h: Uint8Array | undefined): TweakedPublicKey | null;
+export {};
diff --git a/src/cjs/payments/embed.cjs b/src/cjs/payments/embed.cjs
new file mode 100644
index 000000000..42034a92c
--- /dev/null
+++ b/src/cjs/payments/embed.cjs
@@ -0,0 +1,97 @@
+'use strict';
+var __createBinding =
+ (this && this.__createBinding) ||
+ (Object.create
+ ? function (o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ var desc = Object.getOwnPropertyDescriptor(m, k);
+ if (
+ !desc ||
+ ('get' in desc ? !m.__esModule : desc.writable || desc.configurable)
+ ) {
+ desc = {
+ enumerable: true,
+ get: function () {
+ return m[k];
+ },
+ };
+ }
+ Object.defineProperty(o, k2, desc);
+ }
+ : function (o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ o[k2] = m[k];
+ });
+var __setModuleDefault =
+ (this && this.__setModuleDefault) ||
+ (Object.create
+ ? function (o, v) {
+ Object.defineProperty(o, 'default', { enumerable: true, value: v });
+ }
+ : function (o, v) {
+ o['default'] = v;
+ });
+var __importStar =
+ (this && this.__importStar) ||
+ function (mod) {
+ if (mod && mod.__esModule) return mod;
+ var result = {};
+ if (mod != null)
+ for (var k in mod)
+ if (k !== 'default' && Object.prototype.hasOwnProperty.call(mod, k))
+ __createBinding(result, mod, k);
+ __setModuleDefault(result, mod);
+ return result;
+ };
+Object.defineProperty(exports, '__esModule', { value: true });
+exports.p2data = p2data;
+const networks_js_1 = require('../networks.cjs');
+const bscript = __importStar(require('../script.cjs'));
+const types_js_1 = require('../types.cjs');
+const lazy = __importStar(require('./lazy.cjs'));
+const v = __importStar(require('valibot'));
+const OPS = bscript.OPS;
+// output: OP_RETURN ...
+/**
+ * Embeds data in a Bitcoin payment.
+ * @param a - The payment object.
+ * @param opts - Optional payment options.
+ * @returns The modified payment object.
+ * @throws {TypeError} If there is not enough data or if the output is invalid.
+ */
+function p2data(a, opts) {
+ if (!a.data && !a.output) throw new TypeError('Not enough data');
+ opts = Object.assign({ validate: true }, opts || {});
+ v.parse(
+ v.partial(
+ v.object({
+ network: v.object({}),
+ output: types_js_1.BufferSchema,
+ data: v.array(types_js_1.BufferSchema),
+ }),
+ ),
+ a,
+ );
+ const network = a.network || networks_js_1.bitcoin;
+ const o = { name: 'embed', network };
+ lazy.prop(o, 'output', () => {
+ if (!a.data) return;
+ return bscript.compile([OPS.OP_RETURN].concat(a.data));
+ });
+ lazy.prop(o, 'data', () => {
+ if (!a.output) return;
+ return bscript.decompile(a.output).slice(1);
+ });
+ // extended validation
+ if (opts.validate) {
+ if (a.output) {
+ const chunks = bscript.decompile(a.output);
+ if (chunks[0] !== OPS.OP_RETURN) throw new TypeError('Output is invalid');
+ if (!chunks.slice(1).every(chunk => v.is(types_js_1.BufferSchema, chunk)))
+ throw new TypeError('Output is invalid');
+ if (a.data && !(0, types_js_1.stacksEqual)(a.data, o.data))
+ throw new TypeError('Data mismatch');
+ }
+ }
+ return Object.assign(o, a);
+}
diff --git a/src/cjs/payments/embed.d.ts b/src/cjs/payments/embed.d.ts
new file mode 100644
index 000000000..790c13d06
--- /dev/null
+++ b/src/cjs/payments/embed.d.ts
@@ -0,0 +1,9 @@
+import { Payment, PaymentOpts } from './index.js';
+/**
+ * Embeds data in a Bitcoin payment.
+ * @param a - The payment object.
+ * @param opts - Optional payment options.
+ * @returns The modified payment object.
+ * @throws {TypeError} If there is not enough data or if the output is invalid.
+ */
+export declare function p2data(a: Payment, opts?: PaymentOpts): Payment;
diff --git a/src/cjs/payments/index.cjs b/src/cjs/payments/index.cjs
new file mode 100644
index 000000000..2db18ed77
--- /dev/null
+++ b/src/cjs/payments/index.cjs
@@ -0,0 +1,69 @@
+'use strict';
+Object.defineProperty(exports, '__esModule', { value: true });
+exports.p2tr =
+ exports.p2wsh =
+ exports.p2wpkh =
+ exports.p2sh =
+ exports.p2pkh =
+ exports.p2pk =
+ exports.p2ms =
+ exports.embed =
+ void 0;
+const embed_js_1 = require('./embed.cjs');
+Object.defineProperty(exports, 'embed', {
+ enumerable: true,
+ get: function () {
+ return embed_js_1.p2data;
+ },
+});
+const p2ms_js_1 = require('./p2ms.cjs');
+Object.defineProperty(exports, 'p2ms', {
+ enumerable: true,
+ get: function () {
+ return p2ms_js_1.p2ms;
+ },
+});
+const p2pk_js_1 = require('./p2pk.cjs');
+Object.defineProperty(exports, 'p2pk', {
+ enumerable: true,
+ get: function () {
+ return p2pk_js_1.p2pk;
+ },
+});
+const p2pkh_js_1 = require('./p2pkh.cjs');
+Object.defineProperty(exports, 'p2pkh', {
+ enumerable: true,
+ get: function () {
+ return p2pkh_js_1.p2pkh;
+ },
+});
+const p2sh_js_1 = require('./p2sh.cjs');
+Object.defineProperty(exports, 'p2sh', {
+ enumerable: true,
+ get: function () {
+ return p2sh_js_1.p2sh;
+ },
+});
+const p2wpkh_js_1 = require('./p2wpkh.cjs');
+Object.defineProperty(exports, 'p2wpkh', {
+ enumerable: true,
+ get: function () {
+ return p2wpkh_js_1.p2wpkh;
+ },
+});
+const p2wsh_js_1 = require('./p2wsh.cjs');
+Object.defineProperty(exports, 'p2wsh', {
+ enumerable: true,
+ get: function () {
+ return p2wsh_js_1.p2wsh;
+ },
+});
+const p2tr_js_1 = require('./p2tr.cjs');
+Object.defineProperty(exports, 'p2tr', {
+ enumerable: true,
+ get: function () {
+ return p2tr_js_1.p2tr;
+ },
+});
+// TODO
+// witness commitment
diff --git a/src/cjs/payments/index.d.ts b/src/cjs/payments/index.d.ts
new file mode 100644
index 000000000..6be3c722d
--- /dev/null
+++ b/src/cjs/payments/index.d.ts
@@ -0,0 +1,55 @@
+/**
+ * Provides functionality for creating and managing Bitcoin payment objects.
+ *
+ * This module supports multiple Bitcoin address types for payments, including:
+ * - P2PKH (Pay-to-PubKey-Hash)
+ * - P2SH (Pay-to-Script-Hash)
+ * - P2WPKH (Pay-to-Witness-PubKey-Hash)
+ * - P2WSH (Pay-to-Witness-Script-Hash)
+ * - P2TR (Taproot)
+ *
+ * The `Payment` interface defines the structure of a payment object used for constructing various
+ * payment types, with fields for signatures, public keys, redeem scripts, and more.
+ *
+ * @packageDocumentation
+ */
+import { Network } from '../networks.js';
+import { Taptree } from '../types.js';
+import { p2data as embed } from './embed.js';
+import { p2ms } from './p2ms.js';
+import { p2pk } from './p2pk.js';
+import { p2pkh } from './p2pkh.js';
+import { p2sh } from './p2sh.js';
+import { p2wpkh } from './p2wpkh.js';
+import { p2wsh } from './p2wsh.js';
+import { p2tr } from './p2tr.js';
+export interface Payment {
+ name?: string;
+ network?: Network;
+ output?: Uint8Array;
+ data?: Uint8Array[];
+ m?: number;
+ n?: number;
+ pubkeys?: Uint8Array[];
+ input?: Uint8Array;
+ signatures?: Uint8Array[];
+ internalPubkey?: Uint8Array;
+ pubkey?: Uint8Array;
+ signature?: Uint8Array;
+ address?: string;
+ hash?: Uint8Array;
+ redeem?: Payment;
+ redeemVersion?: number;
+ scriptTree?: Taptree;
+ witness?: Uint8Array[];
+}
+export type PaymentCreator = (a: Payment, opts?: PaymentOpts) => Payment;
+export type PaymentFunction = () => Payment;
+export interface PaymentOpts {
+ validate?: boolean;
+ allowIncomplete?: boolean;
+}
+export type StackElement = Uint8Array | number;
+export type Stack = StackElement[];
+export type StackFunction = () => Stack;
+export { embed, p2ms, p2pk, p2pkh, p2sh, p2wpkh, p2wsh, p2tr };
diff --git a/src/payments/lazy.js b/src/cjs/payments/lazy.cjs
similarity index 94%
rename from src/payments/lazy.js
rename to src/cjs/payments/lazy.cjs
index e620c72a9..489d45991 100644
--- a/src/payments/lazy.js
+++ b/src/cjs/payments/lazy.cjs
@@ -1,6 +1,7 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
-exports.value = exports.prop = void 0;
+exports.prop = prop;
+exports.value = value;
function prop(object, name, f) {
Object.defineProperty(object, name, {
configurable: true,
@@ -20,7 +21,6 @@ function prop(object, name, f) {
},
});
}
-exports.prop = prop;
function value(f) {
let _value;
return () => {
@@ -29,4 +29,3 @@ function value(f) {
return _value;
};
}
-exports.value = value;
diff --git a/src/cjs/payments/lazy.d.ts b/src/cjs/payments/lazy.d.ts
new file mode 100644
index 000000000..88bb89a39
--- /dev/null
+++ b/src/cjs/payments/lazy.d.ts
@@ -0,0 +1,2 @@
+export declare function prop(object: object, name: string, f: () => any): void;
+export declare function value(f: () => T): () => T;
diff --git a/src/cjs/payments/p2ms.cjs b/src/cjs/payments/p2ms.cjs
new file mode 100644
index 000000000..9a40c7077
--- /dev/null
+++ b/src/cjs/payments/p2ms.cjs
@@ -0,0 +1,220 @@
+'use strict';
+var __createBinding =
+ (this && this.__createBinding) ||
+ (Object.create
+ ? function (o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ var desc = Object.getOwnPropertyDescriptor(m, k);
+ if (
+ !desc ||
+ ('get' in desc ? !m.__esModule : desc.writable || desc.configurable)
+ ) {
+ desc = {
+ enumerable: true,
+ get: function () {
+ return m[k];
+ },
+ };
+ }
+ Object.defineProperty(o, k2, desc);
+ }
+ : function (o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ o[k2] = m[k];
+ });
+var __setModuleDefault =
+ (this && this.__setModuleDefault) ||
+ (Object.create
+ ? function (o, v) {
+ Object.defineProperty(o, 'default', { enumerable: true, value: v });
+ }
+ : function (o, v) {
+ o['default'] = v;
+ });
+var __importStar =
+ (this && this.__importStar) ||
+ function (mod) {
+ if (mod && mod.__esModule) return mod;
+ var result = {};
+ if (mod != null)
+ for (var k in mod)
+ if (k !== 'default' && Object.prototype.hasOwnProperty.call(mod, k))
+ __createBinding(result, mod, k);
+ __setModuleDefault(result, mod);
+ return result;
+ };
+Object.defineProperty(exports, '__esModule', { value: true });
+exports.p2ms = p2ms;
+const networks_js_1 = require('../networks.cjs');
+const bscript = __importStar(require('../script.cjs'));
+const scriptNumber = __importStar(require('../script_number.cjs'));
+const types_js_1 = require('../types.cjs');
+const lazy = __importStar(require('./lazy.cjs'));
+const v = __importStar(require('valibot'));
+const OPS = bscript.OPS;
+const OP_INT_BASE = OPS.OP_RESERVED; // OP_1 - 1
+function encodeSmallOrScriptNum(n) {
+ return n <= 16 ? OP_INT_BASE + n : scriptNumber.encode(n);
+}
+function decodeSmallOrScriptNum(chunk) {
+ if (typeof chunk === 'number') {
+ const val = chunk - OP_INT_BASE;
+ if (val < 1 || val > 16)
+ throw new TypeError(`Invalid opcode: expected OP_1–OP_16, got ${chunk}`);
+ return val;
+ } else return scriptNumber.decode(chunk);
+}
+function isSmallOrScriptNum(chunk) {
+ if (typeof chunk === 'number')
+ return chunk - OP_INT_BASE >= 1 && chunk - OP_INT_BASE <= 16;
+ else return Number.isInteger(scriptNumber.decode(chunk));
+}
+// input: OP_0 [signatures ...]
+// output: m [pubKeys ...] n OP_CHECKMULTISIG
+/**
+ * Represents a function that creates a Pay-to-Multisig (P2MS) payment object.
+ * @param a - The payment object.
+ * @param opts - Optional payment options.
+ * @returns The created payment object.
+ * @throws {TypeError} If the provided data is not valid.
+ */
+function p2ms(a, opts) {
+ if (
+ !a.input &&
+ !a.output &&
+ !(a.pubkeys && a.m !== undefined) &&
+ !a.signatures
+ )
+ throw new TypeError('Not enough data');
+ opts = Object.assign({ validate: true }, opts || {});
+ function isAcceptableSignature(x) {
+ return (
+ bscript.isCanonicalScriptSignature(x) ||
+ (opts.allowIncomplete && x === OPS.OP_0) !== undefined
+ );
+ }
+ v.parse(
+ v.partial(
+ v.object({
+ network: v.object({}),
+ m: v.number(),
+ n: v.number(),
+ output: types_js_1.BufferSchema,
+ pubkeys: v.array(
+ v.custom(types_js_1.isPoint),
+ 'Received invalid pubkey',
+ ),
+ signatures: v.array(
+ v.custom(isAcceptableSignature),
+ 'Expected signature to be of type isAcceptableSignature',
+ ),
+ input: types_js_1.BufferSchema,
+ }),
+ ),
+ a,
+ );
+ const network = a.network || networks_js_1.bitcoin;
+ const o = { network };
+ let chunks = [];
+ let decoded = false;
+ function decode(output) {
+ if (decoded) return;
+ decoded = true;
+ chunks = bscript.decompile(output);
+ if (chunks.length < 3) throw new TypeError('Output is invalid');
+ o.m = decodeSmallOrScriptNum(chunks[0]);
+ o.n = decodeSmallOrScriptNum(chunks[chunks.length - 2]);
+ o.pubkeys = chunks.slice(1, -2);
+ }
+ lazy.prop(o, 'output', () => {
+ if (!a.m) return;
+ if (!o.n) return;
+ if (!a.pubkeys) return;
+ return bscript.compile(
+ [].concat(
+ encodeSmallOrScriptNum(a.m),
+ a.pubkeys,
+ encodeSmallOrScriptNum(o.n),
+ OPS.OP_CHECKMULTISIG,
+ ),
+ );
+ });
+ lazy.prop(o, 'm', () => {
+ if (!o.output) return;
+ decode(o.output);
+ return o.m;
+ });
+ lazy.prop(o, 'n', () => {
+ if (!o.pubkeys) return;
+ return o.pubkeys.length;
+ });
+ lazy.prop(o, 'pubkeys', () => {
+ if (!a.output) return;
+ decode(a.output);
+ return o.pubkeys;
+ });
+ lazy.prop(o, 'signatures', () => {
+ if (!a.input) return;
+ return bscript.decompile(a.input).slice(1);
+ });
+ lazy.prop(o, 'input', () => {
+ if (!a.signatures) return;
+ return bscript.compile([OPS.OP_0].concat(a.signatures));
+ });
+ lazy.prop(o, 'witness', () => {
+ if (!o.input) return;
+ return [];
+ });
+ lazy.prop(o, 'name', () => {
+ if (!o.m || !o.n) return;
+ return `p2ms(${o.m} of ${o.n})`;
+ });
+ // extended validation
+ if (opts.validate) {
+ if (a.output) {
+ decode(a.output);
+ if (!isSmallOrScriptNum(chunks[0]))
+ throw new TypeError('Output is invalid');
+ if (!isSmallOrScriptNum(chunks[chunks.length - 2]))
+ throw new TypeError('Output is invalid');
+ if (chunks[chunks.length - 1] !== OPS.OP_CHECKMULTISIG)
+ throw new TypeError('Output is invalid');
+ if (o.m <= 0 || o.n > 20 || o.m > o.n || o.n !== chunks.length - 3)
+ throw new TypeError('Output is invalid');
+ if (!o.pubkeys.every(x => (0, types_js_1.isPoint)(x)))
+ throw new TypeError('Output is invalid');
+ if (a.m !== undefined && a.m !== o.m) throw new TypeError('m mismatch');
+ if (a.n !== undefined && a.n !== o.n) throw new TypeError('n mismatch');
+ if (a.pubkeys && !(0, types_js_1.stacksEqual)(a.pubkeys, o.pubkeys))
+ throw new TypeError('Pubkeys mismatch');
+ }
+ if (a.pubkeys) {
+ if (a.n !== undefined && a.n !== a.pubkeys.length)
+ throw new TypeError('Pubkey count mismatch');
+ o.n = a.pubkeys.length;
+ if (o.n < o.m) throw new TypeError('Pubkey count cannot be less than m');
+ }
+ if (a.signatures) {
+ if (a.signatures.length < o.m)
+ throw new TypeError('Not enough signatures provided');
+ if (a.signatures.length > o.m)
+ throw new TypeError('Too many signatures provided');
+ }
+ if (a.input) {
+ if (a.input[0] !== OPS.OP_0) throw new TypeError('Input is invalid');
+ if (
+ o.signatures.length === 0 ||
+ !o.signatures.every(isAcceptableSignature)
+ )
+ throw new TypeError('Input has invalid signature(s)');
+ if (
+ a.signatures &&
+ !(0, types_js_1.stacksEqual)(a.signatures, o.signatures)
+ )
+ throw new TypeError('Signature mismatch');
+ if (a.m !== undefined && a.m !== a.signatures.length)
+ throw new TypeError('Signature count mismatch');
+ }
+ }
+ return Object.assign(o, a);
+}
diff --git a/src/cjs/payments/p2ms.d.ts b/src/cjs/payments/p2ms.d.ts
new file mode 100644
index 000000000..4ec9b69fb
--- /dev/null
+++ b/src/cjs/payments/p2ms.d.ts
@@ -0,0 +1,9 @@
+import { Payment, PaymentOpts } from './index.js';
+/**
+ * Represents a function that creates a Pay-to-Multisig (P2MS) payment object.
+ * @param a - The payment object.
+ * @param opts - Optional payment options.
+ * @returns The created payment object.
+ * @throws {TypeError} If the provided data is not valid.
+ */
+export declare function p2ms(a: Payment, opts?: PaymentOpts): Payment;
diff --git a/src/cjs/payments/p2pk.cjs b/src/cjs/payments/p2pk.cjs
new file mode 100644
index 000000000..65310dbc3
--- /dev/null
+++ b/src/cjs/payments/p2pk.cjs
@@ -0,0 +1,130 @@
+'use strict';
+var __createBinding =
+ (this && this.__createBinding) ||
+ (Object.create
+ ? function (o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ var desc = Object.getOwnPropertyDescriptor(m, k);
+ if (
+ !desc ||
+ ('get' in desc ? !m.__esModule : desc.writable || desc.configurable)
+ ) {
+ desc = {
+ enumerable: true,
+ get: function () {
+ return m[k];
+ },
+ };
+ }
+ Object.defineProperty(o, k2, desc);
+ }
+ : function (o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ o[k2] = m[k];
+ });
+var __setModuleDefault =
+ (this && this.__setModuleDefault) ||
+ (Object.create
+ ? function (o, v) {
+ Object.defineProperty(o, 'default', { enumerable: true, value: v });
+ }
+ : function (o, v) {
+ o['default'] = v;
+ });
+var __importStar =
+ (this && this.__importStar) ||
+ function (mod) {
+ if (mod && mod.__esModule) return mod;
+ var result = {};
+ if (mod != null)
+ for (var k in mod)
+ if (k !== 'default' && Object.prototype.hasOwnProperty.call(mod, k))
+ __createBinding(result, mod, k);
+ __setModuleDefault(result, mod);
+ return result;
+ };
+Object.defineProperty(exports, '__esModule', { value: true });
+exports.p2pk = p2pk;
+const networks_js_1 = require('../networks.cjs');
+const bscript = __importStar(require('../script.cjs'));
+const types_js_1 = require('../types.cjs');
+const lazy = __importStar(require('./lazy.cjs'));
+const tools = __importStar(require('uint8array-tools'));
+const v = __importStar(require('valibot'));
+const OPS = bscript.OPS;
+// input: {signature}
+// output: {pubKey} OP_CHECKSIG
+/**
+ * Creates a pay-to-public-key (P2PK) payment object.
+ *
+ * @param a - The payment object containing the necessary data.
+ * @param opts - Optional payment options.
+ * @returns The P2PK payment object.
+ * @throws {TypeError} If the required data is not provided or if the data is invalid.
+ */
+function p2pk(a, opts) {
+ if (!a.input && !a.output && !a.pubkey && !a.input && !a.signature)
+ throw new TypeError('Not enough data');
+ opts = Object.assign({ validate: true }, opts || {});
+ v.parse(
+ v.partial(
+ v.object({
+ network: v.object({}),
+ output: types_js_1.BufferSchema,
+ pubkey: v.custom(types_js_1.isPoint, 'invalid pubkey'),
+ signature: v.custom(
+ bscript.isCanonicalScriptSignature,
+ 'Expected signature to be of type isCanonicalScriptSignature',
+ ),
+ input: types_js_1.BufferSchema,
+ }),
+ ),
+ a,
+ );
+ const _chunks = lazy.value(() => {
+ return bscript.decompile(a.input);
+ });
+ const network = a.network || networks_js_1.bitcoin;
+ const o = { name: 'p2pk', network };
+ lazy.prop(o, 'output', () => {
+ if (!a.pubkey) return;
+ return bscript.compile([a.pubkey, OPS.OP_CHECKSIG]);
+ });
+ lazy.prop(o, 'pubkey', () => {
+ if (!a.output) return;
+ return a.output.slice(1, -1);
+ });
+ lazy.prop(o, 'signature', () => {
+ if (!a.input) return;
+ return _chunks()[0];
+ });
+ lazy.prop(o, 'input', () => {
+ if (!a.signature) return;
+ return bscript.compile([a.signature]);
+ });
+ lazy.prop(o, 'witness', () => {
+ if (!o.input) return;
+ return [];
+ });
+ // extended validation
+ if (opts.validate) {
+ if (a.output) {
+ if (a.output[a.output.length - 1] !== OPS.OP_CHECKSIG)
+ throw new TypeError('Output is invalid');
+ if (!(0, types_js_1.isPoint)(o.pubkey))
+ throw new TypeError('Output pubkey is invalid');
+ if (a.pubkey && tools.compare(a.pubkey, o.pubkey) !== 0)
+ throw new TypeError('Pubkey mismatch');
+ }
+ if (a.signature) {
+ if (a.input && tools.compare(a.input, o.input) !== 0)
+ throw new TypeError('Signature mismatch');
+ }
+ if (a.input) {
+ if (_chunks().length !== 1) throw new TypeError('Input is invalid');
+ if (!bscript.isCanonicalScriptSignature(o.signature))
+ throw new TypeError('Input has invalid signature');
+ }
+ }
+ return Object.assign(o, a);
+}
diff --git a/src/cjs/payments/p2pk.d.ts b/src/cjs/payments/p2pk.d.ts
new file mode 100644
index 000000000..6f4b13c74
--- /dev/null
+++ b/src/cjs/payments/p2pk.d.ts
@@ -0,0 +1,10 @@
+import { Payment, PaymentOpts } from './index.js';
+/**
+ * Creates a pay-to-public-key (P2PK) payment object.
+ *
+ * @param a - The payment object containing the necessary data.
+ * @param opts - Optional payment options.
+ * @returns The P2PK payment object.
+ * @throws {TypeError} If the required data is not provided or if the data is invalid.
+ */
+export declare function p2pk(a: Payment, opts?: PaymentOpts): Payment;
diff --git a/src/cjs/payments/p2pkh.cjs b/src/cjs/payments/p2pkh.cjs
new file mode 100644
index 000000000..2ec52c192
--- /dev/null
+++ b/src/cjs/payments/p2pkh.cjs
@@ -0,0 +1,192 @@
+'use strict';
+var __createBinding =
+ (this && this.__createBinding) ||
+ (Object.create
+ ? function (o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ var desc = Object.getOwnPropertyDescriptor(m, k);
+ if (
+ !desc ||
+ ('get' in desc ? !m.__esModule : desc.writable || desc.configurable)
+ ) {
+ desc = {
+ enumerable: true,
+ get: function () {
+ return m[k];
+ },
+ };
+ }
+ Object.defineProperty(o, k2, desc);
+ }
+ : function (o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ o[k2] = m[k];
+ });
+var __setModuleDefault =
+ (this && this.__setModuleDefault) ||
+ (Object.create
+ ? function (o, v) {
+ Object.defineProperty(o, 'default', { enumerable: true, value: v });
+ }
+ : function (o, v) {
+ o['default'] = v;
+ });
+var __importStar =
+ (this && this.__importStar) ||
+ function (mod) {
+ if (mod && mod.__esModule) return mod;
+ var result = {};
+ if (mod != null)
+ for (var k in mod)
+ if (k !== 'default' && Object.prototype.hasOwnProperty.call(mod, k))
+ __createBinding(result, mod, k);
+ __setModuleDefault(result, mod);
+ return result;
+ };
+var __importDefault =
+ (this && this.__importDefault) ||
+ function (mod) {
+ return mod && mod.__esModule ? mod : { default: mod };
+ };
+Object.defineProperty(exports, '__esModule', { value: true });
+exports.p2pkh = p2pkh;
+const bcrypto = __importStar(require('../crypto.cjs'));
+const networks_js_1 = require('../networks.cjs');
+const bscript = __importStar(require('../script.cjs'));
+const types_js_1 = require('../types.cjs');
+const lazy = __importStar(require('./lazy.cjs'));
+const bs58check_1 = __importDefault(require('bs58check'));
+const tools = __importStar(require('uint8array-tools'));
+const v = __importStar(require('valibot'));
+const OPS = bscript.OPS;
+// input: {signature} {pubkey}
+// output: OP_DUP OP_HASH160 {hash160(pubkey)} OP_EQUALVERIFY OP_CHECKSIG
+/**
+ * Creates a Pay-to-Public-Key-Hash (P2PKH) payment object.
+ *
+ * @param a - The payment object containing the necessary data.
+ * @param opts - Optional payment options.
+ * @returns The P2PKH payment object.
+ * @throws {TypeError} If the required data is not provided or if the data is invalid.
+ */
+function p2pkh(a, opts) {
+ if (!a.address && !a.hash && !a.output && !a.pubkey && !a.input)
+ throw new TypeError('Not enough data');
+ opts = Object.assign({ validate: true }, opts || {});
+ v.parse(
+ v.partial(
+ v.object({
+ network: v.object({}),
+ address: v.string(),
+ hash: types_js_1.Hash160bitSchema,
+ output: (0, types_js_1.NBufferSchemaFactory)(25),
+ pubkey: v.custom(types_js_1.isPoint),
+ signature: v.custom(bscript.isCanonicalScriptSignature),
+ input: types_js_1.BufferSchema,
+ }),
+ ),
+ a,
+ );
+ const _address = lazy.value(() => {
+ const payload = bs58check_1.default.decode(a.address);
+ const version = tools.readUInt8(payload, 0);
+ const hash = payload.slice(1);
+ return { version, hash };
+ });
+ const _chunks = lazy.value(() => {
+ return bscript.decompile(a.input);
+ });
+ const network = a.network || networks_js_1.bitcoin;
+ const o = { name: 'p2pkh', network };
+ lazy.prop(o, 'address', () => {
+ if (!o.hash) return;
+ const payload = new Uint8Array(21);
+ tools.writeUInt8(payload, 0, network.pubKeyHash);
+ payload.set(o.hash, 1);
+ return bs58check_1.default.encode(payload);
+ });
+ lazy.prop(o, 'hash', () => {
+ if (a.output) return a.output.slice(3, 23);
+ if (a.address) return _address().hash;
+ if (a.pubkey || o.pubkey) return bcrypto.hash160(a.pubkey || o.pubkey);
+ });
+ lazy.prop(o, 'output', () => {
+ if (!o.hash) return;
+ return bscript.compile([
+ OPS.OP_DUP,
+ OPS.OP_HASH160,
+ o.hash,
+ OPS.OP_EQUALVERIFY,
+ OPS.OP_CHECKSIG,
+ ]);
+ });
+ lazy.prop(o, 'pubkey', () => {
+ if (!a.input) return;
+ return _chunks()[1];
+ });
+ lazy.prop(o, 'signature', () => {
+ if (!a.input) return;
+ return _chunks()[0];
+ });
+ lazy.prop(o, 'input', () => {
+ if (!a.pubkey) return;
+ if (!a.signature) return;
+ return bscript.compile([a.signature, a.pubkey]);
+ });
+ lazy.prop(o, 'witness', () => {
+ if (!o.input) return;
+ return [];
+ });
+ // extended validation
+ if (opts.validate) {
+ let hash = Uint8Array.from([]);
+ if (a.address) {
+ if (_address().version !== network.pubKeyHash)
+ throw new TypeError('Invalid version or Network mismatch');
+ if (_address().hash.length !== 20) throw new TypeError('Invalid address');
+ hash = _address().hash;
+ }
+ if (a.hash) {
+ if (hash.length > 0 && tools.compare(hash, a.hash) !== 0)
+ throw new TypeError('Hash mismatch');
+ else hash = a.hash;
+ }
+ if (a.output) {
+ if (
+ a.output.length !== 25 ||
+ a.output[0] !== OPS.OP_DUP ||
+ a.output[1] !== OPS.OP_HASH160 ||
+ a.output[2] !== 0x14 ||
+ a.output[23] !== OPS.OP_EQUALVERIFY ||
+ a.output[24] !== OPS.OP_CHECKSIG
+ )
+ throw new TypeError('Output is invalid');
+ const hash2 = a.output.slice(3, 23);
+ if (hash.length > 0 && tools.compare(hash, hash2) !== 0)
+ throw new TypeError('Hash mismatch');
+ else hash = hash2;
+ }
+ if (a.pubkey) {
+ const pkh = bcrypto.hash160(a.pubkey);
+ if (hash.length > 0 && tools.compare(hash, pkh) !== 0)
+ throw new TypeError('Hash mismatch');
+ else hash = pkh;
+ }
+ if (a.input) {
+ const chunks = _chunks();
+ if (chunks.length !== 2) throw new TypeError('Input is invalid');
+ if (!bscript.isCanonicalScriptSignature(chunks[0]))
+ throw new TypeError('Input has invalid signature');
+ if (!(0, types_js_1.isPoint)(chunks[1]))
+ throw new TypeError('Input has invalid pubkey');
+ if (a.signature && tools.compare(a.signature, chunks[0]) !== 0)
+ throw new TypeError('Signature mismatch');
+ if (a.pubkey && tools.compare(a.pubkey, chunks[1]) !== 0)
+ throw new TypeError('Pubkey mismatch');
+ const pkh = bcrypto.hash160(chunks[1]);
+ if (hash.length > 0 && tools.compare(hash, pkh) !== 0)
+ throw new TypeError('Hash mismatch');
+ }
+ }
+ return Object.assign(o, a);
+}
diff --git a/src/cjs/payments/p2pkh.d.ts b/src/cjs/payments/p2pkh.d.ts
new file mode 100644
index 000000000..e2bdafb1d
--- /dev/null
+++ b/src/cjs/payments/p2pkh.d.ts
@@ -0,0 +1,10 @@
+import { Payment, PaymentOpts } from './index.js';
+/**
+ * Creates a Pay-to-Public-Key-Hash (P2PKH) payment object.
+ *
+ * @param a - The payment object containing the necessary data.
+ * @param opts - Optional payment options.
+ * @returns The P2PKH payment object.
+ * @throws {TypeError} If the required data is not provided or if the data is invalid.
+ */
+export declare function p2pkh(a: Payment, opts?: PaymentOpts): Payment;
diff --git a/src/cjs/payments/p2sh.cjs b/src/cjs/payments/p2sh.cjs
new file mode 100644
index 000000000..979396158
--- /dev/null
+++ b/src/cjs/payments/p2sh.cjs
@@ -0,0 +1,253 @@
+'use strict';
+var __createBinding =
+ (this && this.__createBinding) ||
+ (Object.create
+ ? function (o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ var desc = Object.getOwnPropertyDescriptor(m, k);
+ if (
+ !desc ||
+ ('get' in desc ? !m.__esModule : desc.writable || desc.configurable)
+ ) {
+ desc = {
+ enumerable: true,
+ get: function () {
+ return m[k];
+ },
+ };
+ }
+ Object.defineProperty(o, k2, desc);
+ }
+ : function (o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ o[k2] = m[k];
+ });
+var __setModuleDefault =
+ (this && this.__setModuleDefault) ||
+ (Object.create
+ ? function (o, v) {
+ Object.defineProperty(o, 'default', { enumerable: true, value: v });
+ }
+ : function (o, v) {
+ o['default'] = v;
+ });
+var __importStar =
+ (this && this.__importStar) ||
+ function (mod) {
+ if (mod && mod.__esModule) return mod;
+ var result = {};
+ if (mod != null)
+ for (var k in mod)
+ if (k !== 'default' && Object.prototype.hasOwnProperty.call(mod, k))
+ __createBinding(result, mod, k);
+ __setModuleDefault(result, mod);
+ return result;
+ };
+var __importDefault =
+ (this && this.__importDefault) ||
+ function (mod) {
+ return mod && mod.__esModule ? mod : { default: mod };
+ };
+Object.defineProperty(exports, '__esModule', { value: true });
+exports.p2sh = p2sh;
+const bcrypto = __importStar(require('../crypto.cjs'));
+const networks_js_1 = require('../networks.cjs');
+const bscript = __importStar(require('../script.cjs'));
+const types_js_1 = require('../types.cjs');
+const lazy = __importStar(require('./lazy.cjs'));
+const bs58check_1 = __importDefault(require('bs58check'));
+const tools = __importStar(require('uint8array-tools'));
+const v = __importStar(require('valibot'));
+const OPS = bscript.OPS;
+// input: [redeemScriptSig ...] {redeemScript}
+// witness: >
+// output: OP_HASH160 {hash160(redeemScript)} OP_EQUAL
+/**
+ * Creates a Pay-to-Script-Hash (P2SH) payment object.
+ *
+ * @param a - The payment object containing the necessary data.
+ * @param opts - Optional payment options.
+ * @returns The P2SH payment object.
+ * @throws {TypeError} If the required data is not provided or if the data is invalid.
+ */
+function p2sh(a, opts) {
+ if (!a.address && !a.hash && !a.output && !a.redeem && !a.input)
+ throw new TypeError('Not enough data');
+ opts = Object.assign({ validate: true }, opts || {});
+ v.parse(
+ v.partial(
+ v.object({
+ network: v.object({}),
+ address: v.string(),
+ hash: (0, types_js_1.NBufferSchemaFactory)(20),
+ output: (0, types_js_1.NBufferSchemaFactory)(23),
+ redeem: v.partial(
+ v.object({
+ network: v.object({}),
+ output: types_js_1.BufferSchema,
+ input: types_js_1.BufferSchema,
+ witness: v.array(types_js_1.BufferSchema),
+ }),
+ ),
+ input: types_js_1.BufferSchema,
+ witness: v.array(types_js_1.BufferSchema),
+ }),
+ ),
+ a,
+ );
+ let network = a.network;
+ if (!network) {
+ network = (a.redeem && a.redeem.network) || networks_js_1.bitcoin;
+ }
+ const o = { network };
+ const _address = lazy.value(() => {
+ const payload = bs58check_1.default.decode(a.address);
+ const version = tools.readUInt8(payload, 0);
+ const hash = payload.slice(1);
+ return { version, hash };
+ });
+ const _chunks = lazy.value(() => {
+ return bscript.decompile(a.input);
+ });
+ const _redeem = lazy.value(() => {
+ const chunks = _chunks();
+ const lastChunk = chunks[chunks.length - 1];
+ return {
+ network,
+ output: lastChunk === OPS.OP_FALSE ? Uint8Array.from([]) : lastChunk,
+ input: bscript.compile(chunks.slice(0, -1)),
+ witness: a.witness || [],
+ };
+ });
+ // output dependents
+ lazy.prop(o, 'address', () => {
+ if (!o.hash) return;
+ const payload = new Uint8Array(21);
+ tools.writeUInt8(payload, 0, o.network.scriptHash);
+ payload.set(o.hash, 1);
+ return bs58check_1.default.encode(payload);
+ });
+ lazy.prop(o, 'hash', () => {
+ // in order of least effort
+ if (a.output) return a.output.slice(2, 22);
+ if (a.address) return _address().hash;
+ if (o.redeem && o.redeem.output) return bcrypto.hash160(o.redeem.output);
+ });
+ lazy.prop(o, 'output', () => {
+ if (!o.hash) return;
+ return bscript.compile([OPS.OP_HASH160, o.hash, OPS.OP_EQUAL]);
+ });
+ // input dependents
+ lazy.prop(o, 'redeem', () => {
+ if (!a.input) return;
+ return _redeem();
+ });
+ lazy.prop(o, 'input', () => {
+ if (!a.redeem || !a.redeem.input || !a.redeem.output) return;
+ return bscript.compile(
+ [].concat(bscript.decompile(a.redeem.input), a.redeem.output),
+ );
+ });
+ lazy.prop(o, 'witness', () => {
+ if (o.redeem && o.redeem.witness) return o.redeem.witness;
+ if (o.input) return [];
+ });
+ lazy.prop(o, 'name', () => {
+ const nameParts = ['p2sh'];
+ if (o.redeem !== undefined && o.redeem.name !== undefined)
+ nameParts.push(o.redeem.name);
+ return nameParts.join('-');
+ });
+ if (opts.validate) {
+ let hash = Uint8Array.from([]);
+ if (a.address) {
+ if (_address().version !== network.scriptHash)
+ throw new TypeError('Invalid version or Network mismatch');
+ if (_address().hash.length !== 20) throw new TypeError('Invalid address');
+ hash = _address().hash;
+ }
+ if (a.hash) {
+ if (hash.length > 0 && tools.compare(hash, a.hash) !== 0)
+ throw new TypeError('Hash mismatch');
+ else hash = a.hash;
+ }
+ if (a.output) {
+ if (
+ a.output.length !== 23 ||
+ a.output[0] !== OPS.OP_HASH160 ||
+ a.output[1] !== 0x14 ||
+ a.output[22] !== OPS.OP_EQUAL
+ )
+ throw new TypeError('Output is invalid');
+ const hash2 = a.output.slice(2, 22);
+ if (hash.length > 0 && tools.compare(hash, hash2) !== 0)
+ throw new TypeError('Hash mismatch');
+ else hash = hash2;
+ }
+ // inlined to prevent 'no-inner-declarations' failing
+ const checkRedeem = redeem => {
+ // is the redeem output empty/invalid?
+ if (redeem.output) {
+ const decompile = bscript.decompile(redeem.output);
+ if (!decompile || decompile.length < 1)
+ throw new TypeError('Redeem.output too short');
+ if (redeem.output.byteLength > 520)
+ throw new TypeError(
+ 'Redeem.output unspendable if larger than 520 bytes',
+ );
+ if (bscript.countNonPushOnlyOPs(decompile) > 201)
+ throw new TypeError(
+ 'Redeem.output unspendable with more than 201 non-push ops',
+ );
+ // match hash against other sources
+ const hash2 = bcrypto.hash160(redeem.output);
+ if (hash.length > 0 && tools.compare(hash, hash2) !== 0)
+ throw new TypeError('Hash mismatch');
+ else hash = hash2;
+ }
+ if (redeem.input) {
+ const hasInput = redeem.input.length > 0;
+ const hasWitness = redeem.witness && redeem.witness.length > 0;
+ if (!hasInput && !hasWitness) throw new TypeError('Empty input');
+ if (hasInput && hasWitness)
+ throw new TypeError('Input and witness provided');
+ if (hasInput) {
+ const richunks = bscript.decompile(redeem.input);
+ if (!bscript.isPushOnly(richunks))
+ throw new TypeError('Non push-only scriptSig');
+ }
+ }
+ };
+ if (a.input) {
+ const chunks = _chunks();
+ if (!chunks || chunks.length < 1) throw new TypeError('Input too short');
+ if (!(_redeem().output instanceof Uint8Array))
+ throw new TypeError('Input is invalid');
+ checkRedeem(_redeem());
+ }
+ if (a.redeem) {
+ if (a.redeem.network && a.redeem.network !== network)
+ throw new TypeError('Network mismatch');
+ if (a.input) {
+ const redeem = _redeem();
+ if (
+ a.redeem.output &&
+ tools.compare(a.redeem.output, redeem.output) !== 0
+ )
+ throw new TypeError('Redeem.output mismatch');
+ if (a.redeem.input && tools.compare(a.redeem.input, redeem.input) !== 0)
+ throw new TypeError('Redeem.input mismatch');
+ }
+ checkRedeem(a.redeem);
+ }
+ if (a.witness) {
+ if (
+ a.redeem &&
+ a.redeem.witness &&
+ !(0, types_js_1.stacksEqual)(a.redeem.witness, a.witness)
+ )
+ throw new TypeError('Witness and redeem.witness mismatch');
+ }
+ }
+ return Object.assign(o, a);
+}
diff --git a/src/cjs/payments/p2sh.d.ts b/src/cjs/payments/p2sh.d.ts
new file mode 100644
index 000000000..444c3940a
--- /dev/null
+++ b/src/cjs/payments/p2sh.d.ts
@@ -0,0 +1,10 @@
+import { Payment, PaymentOpts } from './index.js';
+/**
+ * Creates a Pay-to-Script-Hash (P2SH) payment object.
+ *
+ * @param a - The payment object containing the necessary data.
+ * @param opts - Optional payment options.
+ * @returns The P2SH payment object.
+ * @throws {TypeError} If the required data is not provided or if the data is invalid.
+ */
+export declare function p2sh(a: Payment, opts?: PaymentOpts): Payment;
diff --git a/src/cjs/payments/p2tr.cjs b/src/cjs/payments/p2tr.cjs
new file mode 100644
index 000000000..c2c1c74bc
--- /dev/null
+++ b/src/cjs/payments/p2tr.cjs
@@ -0,0 +1,348 @@
+'use strict';
+var __createBinding =
+ (this && this.__createBinding) ||
+ (Object.create
+ ? function (o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ var desc = Object.getOwnPropertyDescriptor(m, k);
+ if (
+ !desc ||
+ ('get' in desc ? !m.__esModule : desc.writable || desc.configurable)
+ ) {
+ desc = {
+ enumerable: true,
+ get: function () {
+ return m[k];
+ },
+ };
+ }
+ Object.defineProperty(o, k2, desc);
+ }
+ : function (o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ o[k2] = m[k];
+ });
+var __setModuleDefault =
+ (this && this.__setModuleDefault) ||
+ (Object.create
+ ? function (o, v) {
+ Object.defineProperty(o, 'default', { enumerable: true, value: v });
+ }
+ : function (o, v) {
+ o['default'] = v;
+ });
+var __importStar =
+ (this && this.__importStar) ||
+ function (mod) {
+ if (mod && mod.__esModule) return mod;
+ var result = {};
+ if (mod != null)
+ for (var k in mod)
+ if (k !== 'default' && Object.prototype.hasOwnProperty.call(mod, k))
+ __createBinding(result, mod, k);
+ __setModuleDefault(result, mod);
+ return result;
+ };
+Object.defineProperty(exports, '__esModule', { value: true });
+exports.p2tr = p2tr;
+const networks_js_1 = require('../networks.cjs');
+const bscript = __importStar(require('../script.cjs'));
+const types_js_1 = require('../types.cjs');
+const ecc_lib_js_1 = require('../ecc_lib.cjs');
+const bip341_js_1 = require('./bip341.cjs');
+const lazy = __importStar(require('./lazy.cjs'));
+const bech32_1 = require('bech32');
+const address_js_1 = require('../address.cjs');
+const tools = __importStar(require('uint8array-tools'));
+const v = __importStar(require('valibot'));
+const OPS = bscript.OPS;
+const TAPROOT_WITNESS_VERSION = 0x01;
+const ANNEX_PREFIX = 0x50;
+/**
+ * Creates a Pay-to-Taproot (P2TR) payment object.
+ *
+ * @param a - The payment object containing the necessary data for P2TR.
+ * @param opts - Optional payment options.
+ * @returns The P2TR payment object.
+ * @throws {TypeError} If the provided data is invalid or insufficient.
+ */
+function p2tr(a, opts) {
+ if (
+ !a.address &&
+ !a.output &&
+ !a.pubkey &&
+ !a.internalPubkey &&
+ !(a.witness && a.witness.length > 1)
+ )
+ throw new TypeError('Not enough data');
+ opts = Object.assign({ validate: true }, opts || {});
+ v.parse(
+ v.partial(
+ v.object({
+ address: v.string(),
+ input: (0, types_js_1.NBufferSchemaFactory)(0),
+ network: v.object({}),
+ output: (0, types_js_1.NBufferSchemaFactory)(34),
+ internalPubkey: (0, types_js_1.NBufferSchemaFactory)(32),
+ hash: (0, types_js_1.NBufferSchemaFactory)(32), // merkle root hash, the tweak
+ pubkey: (0, types_js_1.NBufferSchemaFactory)(32), // tweaked with `hash` from `internalPubkey`
+ signature: v.union([
+ (0, types_js_1.NBufferSchemaFactory)(64),
+ (0, types_js_1.NBufferSchemaFactory)(65),
+ ]),
+ witness: v.array(types_js_1.BufferSchema),
+ scriptTree: v.custom(
+ types_js_1.isTaptree,
+ 'Taptree is not of type isTaptree',
+ ),
+ redeem: v.partial(
+ v.object({
+ output: types_js_1.BufferSchema, // tapleaf script
+ redeemVersion: v.number(), // tapleaf version
+ witness: v.array(types_js_1.BufferSchema),
+ }),
+ ),
+ redeemVersion: v.number(),
+ }),
+ ),
+ a,
+ );
+ const _address = lazy.value(() => {
+ return (0, address_js_1.fromBech32)(a.address);
+ });
+ // remove annex if present, ignored by taproot
+ const _witness = lazy.value(() => {
+ if (!a.witness || !a.witness.length) return;
+ if (
+ a.witness.length >= 2 &&
+ a.witness[a.witness.length - 1][0] === ANNEX_PREFIX
+ ) {
+ return a.witness.slice(0, -1);
+ }
+ return a.witness.slice();
+ });
+ const _hashTree = lazy.value(() => {
+ if (a.scriptTree) return (0, bip341_js_1.toHashTree)(a.scriptTree);
+ if (a.hash) return { hash: a.hash };
+ return;
+ });
+ const network = a.network || networks_js_1.bitcoin;
+ const o = { name: 'p2tr', network };
+ lazy.prop(o, 'address', () => {
+ if (!o.pubkey) return;
+ const words = bech32_1.bech32m.toWords(o.pubkey);
+ words.unshift(TAPROOT_WITNESS_VERSION);
+ return bech32_1.bech32m.encode(network.bech32, words);
+ });
+ lazy.prop(o, 'hash', () => {
+ const hashTree = _hashTree();
+ if (hashTree) return hashTree.hash;
+ const w = _witness();
+ if (w && w.length > 1) {
+ const controlBlock = w[w.length - 1];
+ const leafVersion = controlBlock[0] & types_js_1.TAPLEAF_VERSION_MASK;
+ const script = w[w.length - 2];
+ const leafHash = (0, bip341_js_1.tapleafHash)({
+ output: script,
+ version: leafVersion,
+ });
+ return (0, bip341_js_1.rootHashFromPath)(controlBlock, leafHash);
+ }
+ return null;
+ });
+ lazy.prop(o, 'output', () => {
+ if (!o.pubkey) return;
+ return bscript.compile([OPS.OP_1, o.pubkey]);
+ });
+ lazy.prop(o, 'redeemVersion', () => {
+ if (a.redeemVersion) return a.redeemVersion;
+ if (
+ a.redeem &&
+ a.redeem.redeemVersion !== undefined &&
+ a.redeem.redeemVersion !== null
+ ) {
+ return a.redeem.redeemVersion;
+ }
+ return bip341_js_1.LEAF_VERSION_TAPSCRIPT;
+ });
+ lazy.prop(o, 'redeem', () => {
+ const witness = _witness(); // witness without annex
+ if (!witness || witness.length < 2) return;
+ return {
+ output: witness[witness.length - 2],
+ witness: witness.slice(0, -2),
+ redeemVersion:
+ witness[witness.length - 1][0] & types_js_1.TAPLEAF_VERSION_MASK,
+ };
+ });
+ lazy.prop(o, 'pubkey', () => {
+ if (a.pubkey) return a.pubkey;
+ if (a.output) return a.output.slice(2);
+ if (a.address) return _address().data;
+ if (o.internalPubkey) {
+ const tweakedKey = (0, bip341_js_1.tweakKey)(o.internalPubkey, o.hash);
+ if (tweakedKey) return tweakedKey.x;
+ }
+ });
+ lazy.prop(o, 'internalPubkey', () => {
+ if (a.internalPubkey) return a.internalPubkey;
+ const witness = _witness();
+ if (witness && witness.length > 1)
+ return witness[witness.length - 1].slice(1, 33);
+ });
+ lazy.prop(o, 'signature', () => {
+ if (a.signature) return a.signature;
+ const witness = _witness(); // witness without annex
+ if (!witness || witness.length !== 1) return;
+ return witness[0];
+ });
+ lazy.prop(o, 'witness', () => {
+ if (a.witness) return a.witness;
+ const hashTree = _hashTree();
+ if (hashTree && a.redeem && a.redeem.output && a.internalPubkey) {
+ const leafHash = (0, bip341_js_1.tapleafHash)({
+ output: a.redeem.output,
+ version: o.redeemVersion,
+ });
+ const path = (0, bip341_js_1.findScriptPath)(hashTree, leafHash);
+ if (!path) return;
+ const outputKey = (0, bip341_js_1.tweakKey)(
+ a.internalPubkey,
+ hashTree.hash,
+ );
+ if (!outputKey) return;
+ const controlBock = tools.concat(
+ [
+ Uint8Array.from([o.redeemVersion | outputKey.parity]),
+ a.internalPubkey,
+ ].concat(path),
+ );
+ return [a.redeem.output, controlBock];
+ }
+ if (a.signature) return [a.signature];
+ });
+ // extended validation
+ if (opts.validate) {
+ let pubkey = Uint8Array.from([]);
+ if (a.address) {
+ if (network && network.bech32 !== _address().prefix)
+ throw new TypeError('Invalid prefix or Network mismatch');
+ if (_address().version !== TAPROOT_WITNESS_VERSION)
+ throw new TypeError('Invalid address version');
+ if (_address().data.length !== 32)
+ throw new TypeError('Invalid address data');
+ pubkey = _address().data;
+ }
+ if (a.pubkey) {
+ if (pubkey.length > 0 && tools.compare(pubkey, a.pubkey) !== 0)
+ throw new TypeError('Pubkey mismatch');
+ else pubkey = a.pubkey;
+ }
+ if (a.output) {
+ if (
+ a.output.length !== 34 ||
+ a.output[0] !== OPS.OP_1 ||
+ a.output[1] !== 0x20
+ )
+ throw new TypeError('Output is invalid');
+ if (pubkey.length > 0 && tools.compare(pubkey, a.output.slice(2)) !== 0)
+ throw new TypeError('Pubkey mismatch');
+ else pubkey = a.output.slice(2);
+ }
+ if (a.internalPubkey) {
+ const tweakedKey = (0, bip341_js_1.tweakKey)(a.internalPubkey, o.hash);
+ if (pubkey.length > 0 && tools.compare(pubkey, tweakedKey.x) !== 0)
+ throw new TypeError('Pubkey mismatch');
+ else pubkey = tweakedKey.x;
+ }
+ if (pubkey && pubkey.length) {
+ if (!(0, ecc_lib_js_1.getEccLib)().isXOnlyPoint(pubkey))
+ throw new TypeError('Invalid pubkey for p2tr');
+ }
+ const hashTree = _hashTree();
+ if (a.hash && hashTree) {
+ if (tools.compare(a.hash, hashTree.hash) !== 0)
+ throw new TypeError('Hash mismatch');
+ }
+ if (a.redeem && a.redeem.output && hashTree) {
+ const leafHash = (0, bip341_js_1.tapleafHash)({
+ output: a.redeem.output,
+ version: o.redeemVersion,
+ });
+ if (!(0, bip341_js_1.findScriptPath)(hashTree, leafHash))
+ throw new TypeError('Redeem script not in tree');
+ }
+ const witness = _witness();
+ // compare the provided redeem data with the one computed from witness
+ if (a.redeem && o.redeem) {
+ if (a.redeem.redeemVersion) {
+ if (a.redeem.redeemVersion !== o.redeem.redeemVersion)
+ throw new TypeError('Redeem.redeemVersion and witness mismatch');
+ }
+ if (a.redeem.output) {
+ if (bscript.decompile(a.redeem.output).length === 0)
+ throw new TypeError('Redeem.output is invalid');
+ // output redeem is constructed from the witness
+ if (
+ o.redeem.output &&
+ tools.compare(a.redeem.output, o.redeem.output) !== 0
+ )
+ throw new TypeError('Redeem.output and witness mismatch');
+ }
+ if (a.redeem.witness) {
+ if (
+ o.redeem.witness &&
+ !(0, types_js_1.stacksEqual)(a.redeem.witness, o.redeem.witness)
+ )
+ throw new TypeError('Redeem.witness and witness mismatch');
+ }
+ }
+ if (witness && witness.length) {
+ if (witness.length === 1) {
+ // key spending
+ if (a.signature && tools.compare(a.signature, witness[0]) !== 0)
+ throw new TypeError('Signature mismatch');
+ } else {
+ // script path spending
+ const controlBlock = witness[witness.length - 1];
+ if (controlBlock.length < 33)
+ throw new TypeError(
+ `The control-block length is too small. Got ${controlBlock.length}, expected min 33.`,
+ );
+ if ((controlBlock.length - 33) % 32 !== 0)
+ throw new TypeError(
+ `The control-block length of ${controlBlock.length} is incorrect!`,
+ );
+ const m = (controlBlock.length - 33) / 32;
+ if (m > 128)
+ throw new TypeError(
+ `The script path is too long. Got ${m}, expected max 128.`,
+ );
+ const internalPubkey = controlBlock.slice(1, 33);
+ if (
+ a.internalPubkey &&
+ tools.compare(a.internalPubkey, internalPubkey) !== 0
+ )
+ throw new TypeError('Internal pubkey mismatch');
+ if (!(0, ecc_lib_js_1.getEccLib)().isXOnlyPoint(internalPubkey))
+ throw new TypeError('Invalid internalPubkey for p2tr witness');
+ const leafVersion = controlBlock[0] & types_js_1.TAPLEAF_VERSION_MASK;
+ const script = witness[witness.length - 2];
+ const leafHash = (0, bip341_js_1.tapleafHash)({
+ output: script,
+ version: leafVersion,
+ });
+ const hash = (0, bip341_js_1.rootHashFromPath)(controlBlock, leafHash);
+ const outputKey = (0, bip341_js_1.tweakKey)(internalPubkey, hash);
+ if (!outputKey)
+ // todo: needs test data
+ throw new TypeError('Invalid outputKey for p2tr witness');
+ if (pubkey.length && tools.compare(pubkey, outputKey.x) !== 0)
+ throw new TypeError('Pubkey mismatch for p2tr witness');
+ if (outputKey.parity !== (controlBlock[0] & 1))
+ throw new Error('Incorrect parity');
+ }
+ }
+ }
+ return Object.assign(o, a);
+}
diff --git a/src/cjs/payments/p2tr.d.ts b/src/cjs/payments/p2tr.d.ts
new file mode 100644
index 000000000..2030f62e7
--- /dev/null
+++ b/src/cjs/payments/p2tr.d.ts
@@ -0,0 +1,10 @@
+import { Payment, PaymentOpts } from './index.js';
+/**
+ * Creates a Pay-to-Taproot (P2TR) payment object.
+ *
+ * @param a - The payment object containing the necessary data for P2TR.
+ * @param opts - Optional payment options.
+ * @returns The P2TR payment object.
+ * @throws {TypeError} If the provided data is invalid or insufficient.
+ */
+export declare function p2tr(a: Payment, opts?: PaymentOpts): Payment;
diff --git a/src/cjs/payments/p2wpkh.cjs b/src/cjs/payments/p2wpkh.cjs
new file mode 100644
index 000000000..4ad3cdb22
--- /dev/null
+++ b/src/cjs/payments/p2wpkh.cjs
@@ -0,0 +1,186 @@
+'use strict';
+var __createBinding =
+ (this && this.__createBinding) ||
+ (Object.create
+ ? function (o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ var desc = Object.getOwnPropertyDescriptor(m, k);
+ if (
+ !desc ||
+ ('get' in desc ? !m.__esModule : desc.writable || desc.configurable)
+ ) {
+ desc = {
+ enumerable: true,
+ get: function () {
+ return m[k];
+ },
+ };
+ }
+ Object.defineProperty(o, k2, desc);
+ }
+ : function (o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ o[k2] = m[k];
+ });
+var __setModuleDefault =
+ (this && this.__setModuleDefault) ||
+ (Object.create
+ ? function (o, v) {
+ Object.defineProperty(o, 'default', { enumerable: true, value: v });
+ }
+ : function (o, v) {
+ o['default'] = v;
+ });
+var __importStar =
+ (this && this.__importStar) ||
+ function (mod) {
+ if (mod && mod.__esModule) return mod;
+ var result = {};
+ if (mod != null)
+ for (var k in mod)
+ if (k !== 'default' && Object.prototype.hasOwnProperty.call(mod, k))
+ __createBinding(result, mod, k);
+ __setModuleDefault(result, mod);
+ return result;
+ };
+Object.defineProperty(exports, '__esModule', { value: true });
+exports.p2wpkh = p2wpkh;
+const bcrypto = __importStar(require('../crypto.cjs'));
+const networks_js_1 = require('../networks.cjs');
+const bscript = __importStar(require('../script.cjs'));
+const types_js_1 = require('../types.cjs');
+const lazy = __importStar(require('./lazy.cjs'));
+const bech32_1 = require('bech32');
+const tools = __importStar(require('uint8array-tools'));
+const v = __importStar(require('valibot'));
+const OPS = bscript.OPS;
+const EMPTY_BUFFER = new Uint8Array(0);
+// witness: {signature} {pubKey}
+// input: <>
+// output: OP_0 {pubKeyHash}
+/**
+ * Creates a pay-to-witness-public-key-hash (p2wpkh) payment object.
+ *
+ * @param a - The payment object containing the necessary data.
+ * @param opts - Optional payment options.
+ * @returns The p2wpkh payment object.
+ * @throws {TypeError} If the required data is missing or invalid.
+ */
+function p2wpkh(a, opts) {
+ if (!a.address && !a.hash && !a.output && !a.pubkey && !a.witness)
+ throw new TypeError('Not enough data');
+ opts = Object.assign({ validate: true }, opts || {});
+ v.parse(
+ v.partial(
+ v.object({
+ address: v.string(),
+ hash: (0, types_js_1.NBufferSchemaFactory)(20),
+ input: (0, types_js_1.NBufferSchemaFactory)(0),
+ network: v.object({}),
+ output: (0, types_js_1.NBufferSchemaFactory)(22),
+ pubkey: v.custom(types_js_1.isPoint, 'Not a valid pubkey'),
+ signature: v.custom(bscript.isCanonicalScriptSignature),
+ witness: v.array(types_js_1.BufferSchema),
+ }),
+ ),
+ a,
+ );
+ const _address = lazy.value(() => {
+ const result = bech32_1.bech32.decode(a.address);
+ const version = result.words.shift();
+ const data = bech32_1.bech32.fromWords(result.words);
+ return {
+ version,
+ prefix: result.prefix,
+ data: Uint8Array.from(data),
+ };
+ });
+ const network = a.network || networks_js_1.bitcoin;
+ const o = { name: 'p2wpkh', network };
+ lazy.prop(o, 'address', () => {
+ if (!o.hash) return;
+ const words = bech32_1.bech32.toWords(o.hash);
+ words.unshift(0x00);
+ return bech32_1.bech32.encode(network.bech32, words);
+ });
+ lazy.prop(o, 'hash', () => {
+ if (a.output) return a.output.slice(2, 22);
+ if (a.address) return _address().data;
+ if (a.pubkey || o.pubkey) return bcrypto.hash160(a.pubkey || o.pubkey);
+ });
+ lazy.prop(o, 'output', () => {
+ if (!o.hash) return;
+ return bscript.compile([OPS.OP_0, o.hash]);
+ });
+ lazy.prop(o, 'pubkey', () => {
+ if (a.pubkey) return a.pubkey;
+ if (!a.witness) return;
+ return a.witness[1];
+ });
+ lazy.prop(o, 'signature', () => {
+ if (!a.witness) return;
+ return a.witness[0];
+ });
+ lazy.prop(o, 'input', () => {
+ if (!o.witness) return;
+ return EMPTY_BUFFER;
+ });
+ lazy.prop(o, 'witness', () => {
+ if (!a.pubkey) return;
+ if (!a.signature) return;
+ return [a.signature, a.pubkey];
+ });
+ // extended validation
+ if (opts.validate) {
+ let hash = Uint8Array.from([]);
+ if (a.address) {
+ if (network && network.bech32 !== _address().prefix)
+ throw new TypeError('Invalid prefix or Network mismatch');
+ if (_address().version !== 0x00)
+ throw new TypeError('Invalid address version');
+ if (_address().data.length !== 20)
+ throw new TypeError('Invalid address data');
+ hash = _address().data;
+ }
+ if (a.hash) {
+ if (hash.length > 0 && tools.compare(hash, a.hash) !== 0)
+ throw new TypeError('Hash mismatch');
+ else hash = a.hash;
+ }
+ if (a.output) {
+ if (
+ a.output.length !== 22 ||
+ a.output[0] !== OPS.OP_0 ||
+ a.output[1] !== 0x14
+ )
+ throw new TypeError('Output is invalid');
+ if (hash.length > 0 && tools.compare(hash, a.output.slice(2)) !== 0)
+ throw new TypeError('Hash mismatch');
+ else hash = a.output.slice(2);
+ }
+ if (a.pubkey) {
+ const pkh = bcrypto.hash160(a.pubkey);
+ if (hash.length > 0 && tools.compare(hash, pkh) !== 0)
+ throw new TypeError('Hash mismatch');
+ else hash = pkh;
+ if (!(0, types_js_1.isPoint)(a.pubkey) || a.pubkey.length !== 33)
+ throw new TypeError('Invalid pubkey for p2wpkh');
+ }
+ if (a.witness) {
+ if (a.witness.length !== 2) throw new TypeError('Witness is invalid');
+ if (!bscript.isCanonicalScriptSignature(a.witness[0]))
+ throw new TypeError('Witness has invalid signature');
+ if (!(0, types_js_1.isPoint)(a.witness[1]) || a.witness[1].length !== 33)
+ throw new TypeError('Witness has invalid pubkey');
+ if (a.signature && tools.compare(a.signature, a.witness[0]) !== 0)
+ throw new TypeError('Signature mismatch');
+ // if (a.pubkey && !a.pubkey.equals(a.witness[1]))
+ if (a.pubkey && tools.compare(a.pubkey, a.witness[1]) !== 0)
+ throw new TypeError('Pubkey mismatch');
+ const pkh = bcrypto.hash160(a.witness[1]);
+ if (hash.length > 0 && tools.compare(hash, pkh) !== 0)
+ throw new TypeError('Hash mismatch');
+ }
+ }
+ return Object.assign(o, a);
+}
diff --git a/src/cjs/payments/p2wpkh.d.ts b/src/cjs/payments/p2wpkh.d.ts
new file mode 100644
index 000000000..e4817a112
--- /dev/null
+++ b/src/cjs/payments/p2wpkh.d.ts
@@ -0,0 +1,10 @@
+import { Payment, PaymentOpts } from './index.js';
+/**
+ * Creates a pay-to-witness-public-key-hash (p2wpkh) payment object.
+ *
+ * @param a - The payment object containing the necessary data.
+ * @param opts - Optional payment options.
+ * @returns The p2wpkh payment object.
+ * @throws {TypeError} If the required data is missing or invalid.
+ */
+export declare function p2wpkh(a: Payment, opts?: PaymentOpts): Payment;
diff --git a/src/cjs/payments/p2wsh.cjs b/src/cjs/payments/p2wsh.cjs
new file mode 100644
index 000000000..97c63d473
--- /dev/null
+++ b/src/cjs/payments/p2wsh.cjs
@@ -0,0 +1,269 @@
+'use strict';
+var __createBinding =
+ (this && this.__createBinding) ||
+ (Object.create
+ ? function (o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ var desc = Object.getOwnPropertyDescriptor(m, k);
+ if (
+ !desc ||
+ ('get' in desc ? !m.__esModule : desc.writable || desc.configurable)
+ ) {
+ desc = {
+ enumerable: true,
+ get: function () {
+ return m[k];
+ },
+ };
+ }
+ Object.defineProperty(o, k2, desc);
+ }
+ : function (o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ o[k2] = m[k];
+ });
+var __setModuleDefault =
+ (this && this.__setModuleDefault) ||
+ (Object.create
+ ? function (o, v) {
+ Object.defineProperty(o, 'default', { enumerable: true, value: v });
+ }
+ : function (o, v) {
+ o['default'] = v;
+ });
+var __importStar =
+ (this && this.__importStar) ||
+ function (mod) {
+ if (mod && mod.__esModule) return mod;
+ var result = {};
+ if (mod != null)
+ for (var k in mod)
+ if (k !== 'default' && Object.prototype.hasOwnProperty.call(mod, k))
+ __createBinding(result, mod, k);
+ __setModuleDefault(result, mod);
+ return result;
+ };
+Object.defineProperty(exports, '__esModule', { value: true });
+exports.p2wsh = p2wsh;
+const sha256_1 = require('@noble/hashes/sha256');
+const networks_js_1 = require('../networks.cjs');
+const bscript = __importStar(require('../script.cjs'));
+const types_js_1 = require('../types.cjs');
+const lazy = __importStar(require('./lazy.cjs'));
+const bech32_1 = require('bech32');
+const tools = __importStar(require('uint8array-tools'));
+const v = __importStar(require('valibot'));
+const OPS = bscript.OPS;
+const EMPTY_BUFFER = new Uint8Array(0);
+function chunkHasUncompressedPubkey(chunk) {
+ if (
+ chunk instanceof Uint8Array &&
+ chunk.length === 65 &&
+ chunk[0] === 0x04 &&
+ (0, types_js_1.isPoint)(chunk)
+ ) {
+ return true;
+ } else {
+ return false;
+ }
+}
+// input: <>
+// witness: [redeemScriptSig ...] {redeemScript}
+// output: OP_0 {sha256(redeemScript)}
+/**
+ * Creates a Pay-to-Witness-Script-Hash (P2WSH) payment object.
+ *
+ * @param a - The payment object containing the necessary data.
+ * @param opts - Optional payment options.
+ * @returns The P2WSH payment object.
+ * @throws {TypeError} If the required data is missing or invalid.
+ */
+function p2wsh(a, opts) {
+ if (!a.address && !a.hash && !a.output && !a.redeem && !a.witness)
+ throw new TypeError('Not enough data');
+ opts = Object.assign({ validate: true }, opts || {});
+ v.parse(
+ (0, types_js_1.NullablePartial)({
+ network: v.object({}),
+ address: v.string(),
+ hash: types_js_1.Buffer256bitSchema,
+ output: (0, types_js_1.NBufferSchemaFactory)(34),
+ redeem: (0, types_js_1.NullablePartial)({
+ input: types_js_1.BufferSchema,
+ network: v.object({}),
+ output: types_js_1.BufferSchema,
+ witness: v.array(types_js_1.BufferSchema),
+ }),
+ input: (0, types_js_1.NBufferSchemaFactory)(0),
+ witness: v.array(types_js_1.BufferSchema),
+ }),
+ a,
+ );
+ const _address = lazy.value(() => {
+ const result = bech32_1.bech32.decode(a.address);
+ const version = result.words.shift();
+ const data = bech32_1.bech32.fromWords(result.words);
+ return {
+ version,
+ prefix: result.prefix,
+ data: Uint8Array.from(data),
+ };
+ });
+ const _rchunks = lazy.value(() => {
+ return bscript.decompile(a.redeem.input);
+ });
+ let network = a.network;
+ if (!network) {
+ network = (a.redeem && a.redeem.network) || networks_js_1.bitcoin;
+ }
+ const o = { network };
+ lazy.prop(o, 'address', () => {
+ if (!o.hash) return;
+ const words = bech32_1.bech32.toWords(o.hash);
+ words.unshift(0x00);
+ return bech32_1.bech32.encode(network.bech32, words);
+ });
+ lazy.prop(o, 'hash', () => {
+ if (a.output) return a.output.slice(2);
+ if (a.address) return _address().data;
+ if (o.redeem && o.redeem.output)
+ return (0, sha256_1.sha256)(o.redeem.output);
+ });
+ lazy.prop(o, 'output', () => {
+ if (!o.hash) return;
+ return bscript.compile([OPS.OP_0, o.hash]);
+ });
+ lazy.prop(o, 'redeem', () => {
+ if (!a.witness) return;
+ return {
+ output: a.witness[a.witness.length - 1],
+ input: EMPTY_BUFFER,
+ witness: a.witness.slice(0, -1),
+ };
+ });
+ lazy.prop(o, 'input', () => {
+ if (!o.witness) return;
+ return EMPTY_BUFFER;
+ });
+ lazy.prop(o, 'witness', () => {
+ // transform redeem input to witness stack?
+ if (
+ a.redeem &&
+ a.redeem.input &&
+ a.redeem.input.length > 0 &&
+ a.redeem.output &&
+ a.redeem.output.length > 0
+ ) {
+ const stack = bscript.toStack(_rchunks());
+ // assign, and blank the existing input
+ o.redeem = Object.assign({ witness: stack }, a.redeem);
+ o.redeem.input = EMPTY_BUFFER;
+ return [].concat(stack, a.redeem.output);
+ }
+ if (!a.redeem) return;
+ if (!a.redeem.output) return;
+ if (!a.redeem.witness) return;
+ return [].concat(a.redeem.witness, a.redeem.output);
+ });
+ lazy.prop(o, 'name', () => {
+ const nameParts = ['p2wsh'];
+ if (o.redeem !== undefined && o.redeem.name !== undefined)
+ nameParts.push(o.redeem.name);
+ return nameParts.join('-');
+ });
+ // extended validation
+ if (opts.validate) {
+ let hash = Uint8Array.from([]);
+ if (a.address) {
+ if (_address().prefix !== network.bech32)
+ throw new TypeError('Invalid prefix or Network mismatch');
+ if (_address().version !== 0x00)
+ throw new TypeError('Invalid address version');
+ if (_address().data.length !== 32)
+ throw new TypeError('Invalid address data');
+ hash = _address().data;
+ }
+ if (a.hash) {
+ if (hash.length > 0 && tools.compare(hash, a.hash) !== 0)
+ throw new TypeError('Hash mismatch');
+ else hash = a.hash;
+ }
+ if (a.output) {
+ if (
+ a.output.length !== 34 ||
+ a.output[0] !== OPS.OP_0 ||
+ a.output[1] !== 0x20
+ )
+ throw new TypeError('Output is invalid');
+ const hash2 = a.output.slice(2);
+ if (hash.length > 0 && tools.compare(hash, hash2) !== 0)
+ throw new TypeError('Hash mismatch');
+ else hash = hash2;
+ }
+ if (a.redeem) {
+ if (a.redeem.network && a.redeem.network !== network)
+ throw new TypeError('Network mismatch');
+ // is there two redeem sources?
+ if (
+ a.redeem.input &&
+ a.redeem.input.length > 0 &&
+ a.redeem.witness &&
+ a.redeem.witness.length > 0
+ )
+ throw new TypeError('Ambiguous witness source');
+ // is the redeem output non-empty/valid?
+ if (a.redeem.output) {
+ const decompile = bscript.decompile(a.redeem.output);
+ if (!decompile || decompile.length < 1)
+ throw new TypeError('Redeem.output is invalid');
+ if (a.redeem.output.byteLength > 3600)
+ throw new TypeError(
+ 'Redeem.output unspendable if larger than 3600 bytes',
+ );
+ if (bscript.countNonPushOnlyOPs(decompile) > 201)
+ throw new TypeError(
+ 'Redeem.output unspendable with more than 201 non-push ops',
+ );
+ // match hash against other sources
+ const hash2 = (0, sha256_1.sha256)(a.redeem.output);
+ if (hash.length > 0 && tools.compare(hash, hash2) !== 0)
+ throw new TypeError('Hash mismatch');
+ else hash = hash2;
+ }
+ if (a.redeem.input && !bscript.isPushOnly(_rchunks()))
+ throw new TypeError('Non push-only scriptSig');
+ if (
+ a.witness &&
+ a.redeem.witness &&
+ !(0, types_js_1.stacksEqual)(a.witness, a.redeem.witness)
+ )
+ throw new TypeError('Witness and redeem.witness mismatch');
+ if (
+ (a.redeem.input && _rchunks().some(chunkHasUncompressedPubkey)) ||
+ (a.redeem.output &&
+ (bscript.decompile(a.redeem.output) || []).some(
+ chunkHasUncompressedPubkey,
+ ))
+ ) {
+ throw new TypeError(
+ 'redeem.input or redeem.output contains uncompressed pubkey',
+ );
+ }
+ }
+ if (a.witness && a.witness.length > 0) {
+ const wScript = a.witness[a.witness.length - 1];
+ if (
+ a.redeem &&
+ a.redeem.output &&
+ tools.compare(a.redeem.output, wScript) !== 0
+ )
+ throw new TypeError('Witness and redeem.output mismatch');
+ if (
+ a.witness.some(chunkHasUncompressedPubkey) ||
+ (bscript.decompile(wScript) || []).some(chunkHasUncompressedPubkey)
+ )
+ throw new TypeError('Witness contains uncompressed pubkey');
+ }
+ }
+ return Object.assign(o, a);
+}
diff --git a/src/cjs/payments/p2wsh.d.ts b/src/cjs/payments/p2wsh.d.ts
new file mode 100644
index 000000000..315f55f8c
--- /dev/null
+++ b/src/cjs/payments/p2wsh.d.ts
@@ -0,0 +1,10 @@
+import { Payment, PaymentOpts } from './index.js';
+/**
+ * Creates a Pay-to-Witness-Script-Hash (P2WSH) payment object.
+ *
+ * @param a - The payment object containing the necessary data.
+ * @param opts - Optional payment options.
+ * @returns The P2WSH payment object.
+ * @throws {TypeError} If the required data is missing or invalid.
+ */
+export declare function p2wsh(a: Payment, opts?: PaymentOpts): Payment;
diff --git a/src/cjs/psbt.cjs b/src/cjs/psbt.cjs
new file mode 100644
index 000000000..6e3a5a82d
--- /dev/null
+++ b/src/cjs/psbt.cjs
@@ -0,0 +1,1856 @@
+'use strict';
+var __createBinding =
+ (this && this.__createBinding) ||
+ (Object.create
+ ? function (o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ var desc = Object.getOwnPropertyDescriptor(m, k);
+ if (
+ !desc ||
+ ('get' in desc ? !m.__esModule : desc.writable || desc.configurable)
+ ) {
+ desc = {
+ enumerable: true,
+ get: function () {
+ return m[k];
+ },
+ };
+ }
+ Object.defineProperty(o, k2, desc);
+ }
+ : function (o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ o[k2] = m[k];
+ });
+var __setModuleDefault =
+ (this && this.__setModuleDefault) ||
+ (Object.create
+ ? function (o, v) {
+ Object.defineProperty(o, 'default', { enumerable: true, value: v });
+ }
+ : function (o, v) {
+ o['default'] = v;
+ });
+var __importStar =
+ (this && this.__importStar) ||
+ function (mod) {
+ if (mod && mod.__esModule) return mod;
+ var result = {};
+ if (mod != null)
+ for (var k in mod)
+ if (k !== 'default' && Object.prototype.hasOwnProperty.call(mod, k))
+ __createBinding(result, mod, k);
+ __setModuleDefault(result, mod);
+ return result;
+ };
+Object.defineProperty(exports, '__esModule', { value: true });
+exports.Psbt = exports.toXOnly = void 0;
+const bip174_1 = require('bip174');
+const varuint = __importStar(require('varuint-bitcoin'));
+const bip174_2 = require('bip174');
+const address_js_1 = require('./address.cjs');
+const bufferutils_js_1 = require('./bufferutils.cjs');
+const networks_js_1 = require('./networks.cjs');
+const payments = __importStar(require('./payments/index.cjs'));
+const bip341_js_1 = require('./payments/bip341.cjs');
+const bscript = __importStar(require('./script.cjs'));
+const transaction_js_1 = require('./transaction.cjs');
+const bip371_js_1 = require('./psbt/bip371.cjs');
+Object.defineProperty(exports, 'toXOnly', {
+ enumerable: true,
+ get: function () {
+ return bip371_js_1.toXOnly;
+ },
+});
+const psbtutils_js_1 = require('./psbt/psbtutils.cjs');
+const tools = __importStar(require('uint8array-tools'));
+/**
+ * These are the default arguments for a Psbt instance.
+ */
+const DEFAULT_OPTS = {
+ /**
+ * A bitcoinjs Network object. This is only used if you pass an `address`
+ * parameter to addOutput. Otherwise it is not needed and can be left default.
+ */
+ network: networks_js_1.bitcoin,
+ /**
+ * When extractTransaction is called, the fee rate is checked.
+ * THIS IS NOT TO BE RELIED ON.
+ * It is only here as a last ditch effort to prevent sending a 500 BTC fee etc.
+ */
+ maximumFeeRate: 5000, // satoshi per byte
+};
+/**
+ * Psbt class can parse and generate a PSBT binary based off of the BIP174.
+ * There are 6 roles that this class fulfills. (Explained in BIP174)
+ *
+ * Creator: This can be done with `new Psbt()`
+ *
+ * Updater: This can be done with `psbt.addInput(input)`, `psbt.addInputs(inputs)`,
+ * `psbt.addOutput(output)`, `psbt.addOutputs(outputs)` when you are looking to
+ * add new inputs and outputs to the PSBT, and `psbt.updateGlobal(itemObject)`,
+ * `psbt.updateInput(itemObject)`, `psbt.updateOutput(itemObject)`
+ * addInput requires hash: Buffer | string; and index: number; as attributes
+ * and can also include any attributes that are used in updateInput method.
+ * addOutput requires script: Buffer; and value: number; and likewise can include
+ * data for updateOutput.
+ * For a list of what attributes should be what types. Check the bip174 library.
+ * Also, check the integration tests for some examples of usage.
+ *
+ * Signer: There are a few methods. signAllInputs and signAllInputsAsync, which will search all input
+ * information for your pubkey or pubkeyhash, and only sign inputs where it finds
+ * your info. Or you can explicitly sign a specific input with signInput and
+ * signInputAsync. For the async methods you can create a SignerAsync object
+ * and use something like a hardware wallet to sign with. (You must implement this)
+ *
+ * Combiner: psbts can be combined easily with `psbt.combine(psbt2, psbt3, psbt4 ...)`
+ * the psbt calling combine will always have precedence when a conflict occurs.
+ * Combine checks if the internal bitcoin transaction is the same, so be sure that
+ * all sequences, version, locktime, etc. are the same before combining.
+ *
+ * Input Finalizer: This role is fairly important. Not only does it need to construct
+ * the input scriptSigs and witnesses, but it SHOULD verify the signatures etc.
+ * Before running `psbt.finalizeAllInputs()` please run `psbt.validateSignaturesOfAllInputs()`
+ * Running any finalize method will delete any data in the input(s) that are no longer
+ * needed due to the finalized scripts containing the information.
+ *
+ * Transaction Extractor: This role will perform some checks before returning a
+ * Transaction object. Such as fee rate not being larger than maximumFeeRate etc.
+ */
+class Psbt {
+ data;
+ static fromBase64(data, opts = {}) {
+ const buffer = tools.fromBase64(data);
+ return this.fromBuffer(buffer, opts);
+ }
+ static fromHex(data, opts = {}) {
+ const buffer = tools.fromHex(data);
+ return this.fromBuffer(buffer, opts);
+ }
+ static fromBuffer(buffer, opts = {}) {
+ const psbtBase = bip174_1.Psbt.fromBuffer(buffer, transactionFromBuffer);
+ const psbt = new Psbt(opts, psbtBase);
+ checkTxForDupeIns(psbt.__CACHE.__TX, psbt.__CACHE);
+ return psbt;
+ }
+ __CACHE;
+ opts;
+ constructor(opts = {}, data = new bip174_1.Psbt(new PsbtTransaction())) {
+ this.data = data;
+ // set defaults
+ this.opts = Object.assign({}, DEFAULT_OPTS, opts);
+ this.__CACHE = {
+ __NON_WITNESS_UTXO_TX_CACHE: [],
+ __NON_WITNESS_UTXO_BUF_CACHE: [],
+ __TX_IN_CACHE: {},
+ __TX: this.data.globalMap.unsignedTx.tx,
+ // Psbt's predecessor (TransactionBuilder - now removed) behavior
+ // was to not confirm input values before signing.
+ // Even though we highly encourage people to get
+ // the full parent transaction to verify values, the ability to
+ // sign non-segwit inputs without the full transaction was often
+ // requested. So the only way to activate is to use @ts-ignore.
+ // We will disable exporting the Psbt when unsafe sign is active.
+ // because it is not BIP174 compliant.
+ __UNSAFE_SIGN_NONSEGWIT: false,
+ };
+ if (this.data.inputs.length === 0) this.setVersion(2);
+ // Make data hidden when enumerating
+ const dpew = (obj, attr, enumerable, writable) =>
+ Object.defineProperty(obj, attr, {
+ enumerable,
+ writable,
+ });
+ dpew(this, '__CACHE', false, true);
+ dpew(this, 'opts', false, true);
+ }
+ get inputCount() {
+ return this.data.inputs.length;
+ }
+ get version() {
+ return this.__CACHE.__TX.version;
+ }
+ set version(version) {
+ this.setVersion(version);
+ }
+ get locktime() {
+ return this.__CACHE.__TX.locktime;
+ }
+ set locktime(locktime) {
+ this.setLocktime(locktime);
+ }
+ get txInputs() {
+ return this.__CACHE.__TX.ins.map(input => ({
+ hash: (0, bufferutils_js_1.cloneBuffer)(input.hash),
+ index: input.index,
+ sequence: input.sequence,
+ }));
+ }
+ get txOutputs() {
+ return this.__CACHE.__TX.outs.map(output => {
+ let address;
+ try {
+ address = (0, address_js_1.fromOutputScript)(
+ output.script,
+ this.opts.network,
+ );
+ } catch (_) {}
+ return {
+ script: (0, bufferutils_js_1.cloneBuffer)(output.script),
+ value: output.value,
+ address,
+ };
+ });
+ }
+ combine(...those) {
+ this.data.combine(...those.map(o => o.data));
+ return this;
+ }
+ clone() {
+ // TODO: more efficient cloning
+ const res = Psbt.fromBuffer(this.data.toBuffer());
+ res.opts = JSON.parse(JSON.stringify(this.opts));
+ return res;
+ }
+ setMaximumFeeRate(satoshiPerByte) {
+ check32Bit(satoshiPerByte); // 42.9 BTC per byte IS excessive... so throw
+ this.opts.maximumFeeRate = satoshiPerByte;
+ }
+ setVersion(version) {
+ check32Bit(version);
+ checkInputsForPartialSig(this.data.inputs, 'setVersion');
+ const c = this.__CACHE;
+ c.__TX.version = version;
+ c.__EXTRACTED_TX = undefined;
+ return this;
+ }
+ setLocktime(locktime) {
+ check32Bit(locktime);
+ checkInputsForPartialSig(this.data.inputs, 'setLocktime');
+ const c = this.__CACHE;
+ c.__TX.locktime = locktime;
+ c.__EXTRACTED_TX = undefined;
+ return this;
+ }
+ setInputSequence(inputIndex, sequence) {
+ check32Bit(sequence);
+ checkInputsForPartialSig(this.data.inputs, 'setInputSequence');
+ const c = this.__CACHE;
+ if (c.__TX.ins.length <= inputIndex) {
+ throw new Error('Input index too high');
+ }
+ c.__TX.ins[inputIndex].sequence = sequence;
+ c.__EXTRACTED_TX = undefined;
+ return this;
+ }
+ addInputs(inputDatas) {
+ inputDatas.forEach(inputData => this.addInput(inputData));
+ return this;
+ }
+ addInput(inputData) {
+ if (
+ arguments.length > 1 ||
+ !inputData ||
+ inputData.hash === undefined ||
+ inputData.index === undefined
+ ) {
+ throw new Error(
+ `Invalid arguments for Psbt.addInput. ` +
+ `Requires single object with at least [hash] and [index]`,
+ );
+ }
+ (0, bip371_js_1.checkTaprootInputFields)(inputData, inputData, 'addInput');
+ checkInputsForPartialSig(this.data.inputs, 'addInput');
+ if (inputData.witnessScript) checkInvalidP2WSH(inputData.witnessScript);
+ const c = this.__CACHE;
+ this.data.addInput(inputData);
+ const txIn = c.__TX.ins[c.__TX.ins.length - 1];
+ checkTxInputCache(c, txIn);
+ const inputIndex = this.data.inputs.length - 1;
+ const input = this.data.inputs[inputIndex];
+ if (input.nonWitnessUtxo) {
+ addNonWitnessTxCache(this.__CACHE, input, inputIndex);
+ }
+ c.__FEE = undefined;
+ c.__FEE_RATE = undefined;
+ c.__EXTRACTED_TX = undefined;
+ return this;
+ }
+ addOutputs(outputDatas) {
+ outputDatas.forEach(outputData => this.addOutput(outputData));
+ return this;
+ }
+ addOutput(outputData) {
+ if (
+ arguments.length > 1 ||
+ !outputData ||
+ outputData.value === undefined ||
+ (outputData.address === undefined && outputData.script === undefined)
+ ) {
+ throw new Error(
+ `Invalid arguments for Psbt.addOutput. ` +
+ `Requires single object with at least [script or address] and [value]`,
+ );
+ }
+ checkInputsForPartialSig(this.data.inputs, 'addOutput');
+ const { address } = outputData;
+ if (typeof address === 'string') {
+ const { network } = this.opts;
+ const script = (0, address_js_1.toOutputScript)(address, network);
+ outputData = Object.assign({}, outputData, { script });
+ }
+ (0, bip371_js_1.checkTaprootOutputFields)(
+ outputData,
+ outputData,
+ 'addOutput',
+ );
+ const c = this.__CACHE;
+ this.data.addOutput(outputData);
+ c.__FEE = undefined;
+ c.__FEE_RATE = undefined;
+ c.__EXTRACTED_TX = undefined;
+ return this;
+ }
+ extractTransaction(disableFeeCheck) {
+ if (!this.data.inputs.every(isFinalized)) throw new Error('Not finalized');
+ const c = this.__CACHE;
+ if (!disableFeeCheck) {
+ checkFees(this, c, this.opts);
+ }
+ if (c.__EXTRACTED_TX) return c.__EXTRACTED_TX;
+ const tx = c.__TX.clone();
+ inputFinalizeGetAmts(this.data.inputs, tx, c, true);
+ return tx;
+ }
+ getFeeRate() {
+ return getTxCacheValue(
+ '__FEE_RATE',
+ 'fee rate',
+ this.data.inputs,
+ this.__CACHE,
+ );
+ }
+ getFee() {
+ return getTxCacheValue('__FEE', 'fee', this.data.inputs, this.__CACHE);
+ }
+ finalizeAllInputs() {
+ (0, bip174_2.checkForInput)(this.data.inputs, 0); // making sure we have at least one
+ range(this.data.inputs.length).forEach(idx => this.finalizeInput(idx));
+ return this;
+ }
+ finalizeInput(inputIndex, finalScriptsFunc) {
+ const input = (0, bip174_2.checkForInput)(this.data.inputs, inputIndex);
+ if ((0, bip371_js_1.isTaprootInput)(input))
+ return this._finalizeTaprootInput(
+ inputIndex,
+ input,
+ undefined,
+ finalScriptsFunc,
+ );
+ return this._finalizeInput(inputIndex, input, finalScriptsFunc);
+ }
+ finalizeTaprootInput(
+ inputIndex,
+ tapLeafHashToFinalize,
+ finalScriptsFunc = bip371_js_1.tapScriptFinalizer,
+ ) {
+ const input = (0, bip174_2.checkForInput)(this.data.inputs, inputIndex);
+ if ((0, bip371_js_1.isTaprootInput)(input))
+ return this._finalizeTaprootInput(
+ inputIndex,
+ input,
+ tapLeafHashToFinalize,
+ finalScriptsFunc,
+ );
+ throw new Error(`Cannot finalize input #${inputIndex}. Not Taproot.`);
+ }
+ _finalizeInput(inputIndex, input, finalScriptsFunc = getFinalScripts) {
+ const { script, isP2SH, isP2WSH, isSegwit } = getScriptFromInput(
+ inputIndex,
+ input,
+ this.__CACHE,
+ );
+ if (!script) throw new Error(`No script found for input #${inputIndex}`);
+ checkPartialSigSighashes(input);
+ const { finalScriptSig, finalScriptWitness } = finalScriptsFunc(
+ inputIndex,
+ input,
+ script,
+ isSegwit,
+ isP2SH,
+ isP2WSH,
+ );
+ if (finalScriptSig) this.data.updateInput(inputIndex, { finalScriptSig });
+ if (finalScriptWitness)
+ this.data.updateInput(inputIndex, { finalScriptWitness });
+ if (!finalScriptSig && !finalScriptWitness)
+ throw new Error(`Unknown error finalizing input #${inputIndex}`);
+ this.data.clearFinalizedInput(inputIndex);
+ return this;
+ }
+ _finalizeTaprootInput(
+ inputIndex,
+ input,
+ tapLeafHashToFinalize,
+ finalScriptsFunc = bip371_js_1.tapScriptFinalizer,
+ ) {
+ if (!input.witnessUtxo)
+ throw new Error(
+ `Cannot finalize input #${inputIndex}. Missing withness utxo.`,
+ );
+ // Check key spend first. Increased privacy and reduced block space.
+ if (input.tapKeySig) {
+ const payment = payments.p2tr({
+ output: input.witnessUtxo.script,
+ signature: input.tapKeySig,
+ });
+ const finalScriptWitness = (0,
+ psbtutils_js_1.witnessStackToScriptWitness)(payment.witness);
+ this.data.updateInput(inputIndex, { finalScriptWitness });
+ } else {
+ const { finalScriptWitness } = finalScriptsFunc(
+ inputIndex,
+ input,
+ tapLeafHashToFinalize,
+ );
+ this.data.updateInput(inputIndex, { finalScriptWitness });
+ }
+ this.data.clearFinalizedInput(inputIndex);
+ return this;
+ }
+ getInputType(inputIndex) {
+ const input = (0, bip174_2.checkForInput)(this.data.inputs, inputIndex);
+ const script = getScriptFromUtxo(inputIndex, input, this.__CACHE);
+ const result = getMeaningfulScript(
+ script,
+ inputIndex,
+ 'input',
+ input.redeemScript || redeemFromFinalScriptSig(input.finalScriptSig),
+ input.witnessScript ||
+ redeemFromFinalWitnessScript(input.finalScriptWitness),
+ );
+ const type = result.type === 'raw' ? '' : result.type + '-';
+ const mainType = classifyScript(result.meaningfulScript);
+ return type + mainType;
+ }
+ inputHasPubkey(inputIndex, pubkey) {
+ const input = (0, bip174_2.checkForInput)(this.data.inputs, inputIndex);
+ return pubkeyInInput(pubkey, input, inputIndex, this.__CACHE);
+ }
+ inputHasHDKey(inputIndex, root) {
+ const input = (0, bip174_2.checkForInput)(this.data.inputs, inputIndex);
+ const derivationIsMine = bip32DerivationIsMine(root);
+ return (
+ !!input.bip32Derivation && input.bip32Derivation.some(derivationIsMine)
+ );
+ }
+ outputHasPubkey(outputIndex, pubkey) {
+ const output = (0, bip174_2.checkForOutput)(this.data.outputs, outputIndex);
+ return pubkeyInOutput(pubkey, output, outputIndex, this.__CACHE);
+ }
+ outputHasHDKey(outputIndex, root) {
+ const output = (0, bip174_2.checkForOutput)(this.data.outputs, outputIndex);
+ const derivationIsMine = bip32DerivationIsMine(root);
+ return (
+ !!output.bip32Derivation && output.bip32Derivation.some(derivationIsMine)
+ );
+ }
+ validateSignaturesOfAllInputs(validator) {
+ (0, bip174_2.checkForInput)(this.data.inputs, 0); // making sure we have at least one
+ const results = range(this.data.inputs.length).map(idx =>
+ this.validateSignaturesOfInput(idx, validator),
+ );
+ return results.reduce((final, res) => res === true && final, true);
+ }
+ validateSignaturesOfInput(inputIndex, validator, pubkey) {
+ const input = this.data.inputs[inputIndex];
+ if ((0, bip371_js_1.isTaprootInput)(input))
+ return this.validateSignaturesOfTaprootInput(
+ inputIndex,
+ validator,
+ pubkey,
+ );
+ return this._validateSignaturesOfInput(inputIndex, validator, pubkey);
+ }
+ _validateSignaturesOfInput(inputIndex, validator, pubkey) {
+ const input = this.data.inputs[inputIndex];
+ const partialSig = (input || {}).partialSig;
+ if (!input || !partialSig || partialSig.length < 1)
+ throw new Error('No signatures to validate');
+ if (typeof validator !== 'function')
+ throw new Error('Need validator function to validate signatures');
+ const mySigs = pubkey
+ ? partialSig.filter(sig => tools.compare(sig.pubkey, pubkey) === 0)
+ : partialSig;
+ if (mySigs.length < 1) throw new Error('No signatures for this pubkey');
+ const results = [];
+ let hashCache;
+ let scriptCache;
+ let sighashCache;
+ for (const pSig of mySigs) {
+ const sig = bscript.signature.decode(pSig.signature);
+ const { hash, script } =
+ sighashCache !== sig.hashType
+ ? getHashForSig(
+ inputIndex,
+ Object.assign({}, input, { sighashType: sig.hashType }),
+ this.__CACHE,
+ true,
+ )
+ : { hash: hashCache, script: scriptCache };
+ sighashCache = sig.hashType;
+ hashCache = hash;
+ scriptCache = script;
+ checkScriptForPubkey(pSig.pubkey, script, 'verify');
+ results.push(validator(pSig.pubkey, hash, sig.signature));
+ }
+ return results.every(res => res === true);
+ }
+ validateSignaturesOfTaprootInput(inputIndex, validator, pubkey) {
+ const input = this.data.inputs[inputIndex];
+ const tapKeySig = (input || {}).tapKeySig;
+ const tapScriptSig = (input || {}).tapScriptSig;
+ if (!input && !tapKeySig && !(tapScriptSig && !tapScriptSig.length))
+ throw new Error('No signatures to validate');
+ if (typeof validator !== 'function')
+ throw new Error('Need validator function to validate signatures');
+ pubkey = pubkey && (0, bip371_js_1.toXOnly)(pubkey);
+ const allHashses = pubkey
+ ? getTaprootHashesForSigValidation(
+ inputIndex,
+ input,
+ this.data.inputs,
+ pubkey,
+ this.__CACHE,
+ )
+ : getAllTaprootHashesForSigValidation(
+ inputIndex,
+ input,
+ this.data.inputs,
+ this.__CACHE,
+ );
+ if (!allHashses.length) throw new Error('No signatures for this pubkey');
+ const tapKeyHash = allHashses.find(h => !h.leafHash);
+ let validationResultCount = 0;
+ if (tapKeySig && tapKeyHash) {
+ const isValidTapkeySig = validator(
+ tapKeyHash.pubkey,
+ tapKeyHash.hash,
+ trimTaprootSig(tapKeySig),
+ );
+ if (!isValidTapkeySig) return false;
+ validationResultCount++;
+ }
+ if (tapScriptSig) {
+ for (const tapSig of tapScriptSig) {
+ const tapSigHash = allHashses.find(
+ h => tools.compare(h.pubkey, tapSig.pubkey) === 0,
+ );
+ if (tapSigHash) {
+ const isValidTapScriptSig = validator(
+ tapSig.pubkey,
+ tapSigHash.hash,
+ trimTaprootSig(tapSig.signature),
+ );
+ if (!isValidTapScriptSig) return false;
+ validationResultCount++;
+ }
+ }
+ }
+ return validationResultCount > 0;
+ }
+ signAllInputsHD(
+ hdKeyPair,
+ sighashTypes = [transaction_js_1.Transaction.SIGHASH_ALL],
+ ) {
+ if (!hdKeyPair || !hdKeyPair.publicKey || !hdKeyPair.fingerprint) {
+ throw new Error('Need HDSigner to sign input');
+ }
+ const results = [];
+ for (const i of range(this.data.inputs.length)) {
+ try {
+ this.signInputHD(i, hdKeyPair, sighashTypes);
+ results.push(true);
+ } catch (err) {
+ results.push(false);
+ }
+ }
+ if (results.every(v => v === false)) {
+ throw new Error('No inputs were signed');
+ }
+ return this;
+ }
+ signAllInputsHDAsync(
+ hdKeyPair,
+ sighashTypes = [transaction_js_1.Transaction.SIGHASH_ALL],
+ ) {
+ return new Promise((resolve, reject) => {
+ if (!hdKeyPair || !hdKeyPair.publicKey || !hdKeyPair.fingerprint) {
+ return reject(new Error('Need HDSigner to sign input'));
+ }
+ const results = [];
+ const promises = [];
+ for (const i of range(this.data.inputs.length)) {
+ promises.push(
+ this.signInputHDAsync(i, hdKeyPair, sighashTypes).then(
+ () => {
+ results.push(true);
+ },
+ () => {
+ results.push(false);
+ },
+ ),
+ );
+ }
+ return Promise.all(promises).then(() => {
+ if (results.every(v => v === false)) {
+ return reject(new Error('No inputs were signed'));
+ }
+ resolve();
+ });
+ });
+ }
+ signInputHD(
+ inputIndex,
+ hdKeyPair,
+ sighashTypes = [transaction_js_1.Transaction.SIGHASH_ALL],
+ ) {
+ if (!hdKeyPair || !hdKeyPair.publicKey || !hdKeyPair.fingerprint) {
+ throw new Error('Need HDSigner to sign input');
+ }
+ const signers = getSignersFromHD(inputIndex, this.data.inputs, hdKeyPair);
+ signers.forEach(signer => this.signInput(inputIndex, signer, sighashTypes));
+ return this;
+ }
+ signInputHDAsync(
+ inputIndex,
+ hdKeyPair,
+ sighashTypes = [transaction_js_1.Transaction.SIGHASH_ALL],
+ ) {
+ return new Promise((resolve, reject) => {
+ if (!hdKeyPair || !hdKeyPair.publicKey || !hdKeyPair.fingerprint) {
+ return reject(new Error('Need HDSigner to sign input'));
+ }
+ const signers = getSignersFromHD(inputIndex, this.data.inputs, hdKeyPair);
+ const promises = signers.map(signer =>
+ this.signInputAsync(inputIndex, signer, sighashTypes),
+ );
+ return Promise.all(promises)
+ .then(() => {
+ resolve();
+ })
+ .catch(reject);
+ });
+ }
+ signAllInputs(keyPair, sighashTypes) {
+ if (!keyPair || !keyPair.publicKey)
+ throw new Error('Need Signer to sign input');
+ // TODO: Add a pubkey/pubkeyhash cache to each input
+ // as input information is added, then eventually
+ // optimize this method.
+ const results = [];
+ for (const i of range(this.data.inputs.length)) {
+ try {
+ this.signInput(i, keyPair, sighashTypes);
+ results.push(true);
+ } catch (err) {
+ results.push(false);
+ }
+ }
+ if (results.every(v => v === false)) {
+ throw new Error('No inputs were signed');
+ }
+ return this;
+ }
+ signAllInputsAsync(keyPair, sighashTypes) {
+ return new Promise((resolve, reject) => {
+ if (!keyPair || !keyPair.publicKey)
+ return reject(new Error('Need Signer to sign input'));
+ // TODO: Add a pubkey/pubkeyhash cache to each input
+ // as input information is added, then eventually
+ // optimize this method.
+ const results = [];
+ const promises = [];
+ for (const [i] of this.data.inputs.entries()) {
+ promises.push(
+ this.signInputAsync(i, keyPair, sighashTypes).then(
+ () => {
+ results.push(true);
+ },
+ () => {
+ results.push(false);
+ },
+ ),
+ );
+ }
+ return Promise.all(promises).then(() => {
+ if (results.every(v => v === false)) {
+ return reject(new Error('No inputs were signed'));
+ }
+ resolve();
+ });
+ });
+ }
+ signInput(inputIndex, keyPair, sighashTypes) {
+ if (!keyPair || !keyPair.publicKey)
+ throw new Error('Need Signer to sign input');
+ const input = (0, bip174_2.checkForInput)(this.data.inputs, inputIndex);
+ if ((0, bip371_js_1.isTaprootInput)(input)) {
+ return this._signTaprootInput(
+ inputIndex,
+ input,
+ keyPair,
+ undefined,
+ sighashTypes,
+ );
+ }
+ return this._signInput(inputIndex, keyPair, sighashTypes);
+ }
+ signTaprootInput(inputIndex, keyPair, tapLeafHashToSign, sighashTypes) {
+ if (!keyPair || !keyPair.publicKey)
+ throw new Error('Need Signer to sign input');
+ const input = (0, bip174_2.checkForInput)(this.data.inputs, inputIndex);
+ if ((0, bip371_js_1.isTaprootInput)(input))
+ return this._signTaprootInput(
+ inputIndex,
+ input,
+ keyPair,
+ tapLeafHashToSign,
+ sighashTypes,
+ );
+ throw new Error(`Input #${inputIndex} is not of type Taproot.`);
+ }
+ _signInput(
+ inputIndex,
+ keyPair,
+ sighashTypes = [transaction_js_1.Transaction.SIGHASH_ALL],
+ ) {
+ const { hash, sighashType } = getHashAndSighashType(
+ this.data.inputs,
+ inputIndex,
+ keyPair.publicKey,
+ this.__CACHE,
+ sighashTypes,
+ );
+ const partialSig = [
+ {
+ pubkey: keyPair.publicKey,
+ signature: bscript.signature.encode(keyPair.sign(hash), sighashType),
+ },
+ ];
+ this.data.updateInput(inputIndex, { partialSig });
+ return this;
+ }
+ _signTaprootInput(
+ inputIndex,
+ input,
+ keyPair,
+ tapLeafHashToSign,
+ allowedSighashTypes = [transaction_js_1.Transaction.SIGHASH_DEFAULT],
+ ) {
+ const hashesForSig = this.checkTaprootHashesForSig(
+ inputIndex,
+ input,
+ keyPair,
+ tapLeafHashToSign,
+ allowedSighashTypes,
+ );
+ const tapKeySig = hashesForSig
+ .filter(h => !h.leafHash)
+ .map(h =>
+ (0, bip371_js_1.serializeTaprootSignature)(
+ keyPair.signSchnorr(h.hash),
+ input.sighashType,
+ ),
+ )[0];
+ const tapScriptSig = hashesForSig
+ .filter(h => !!h.leafHash)
+ .map(h => ({
+ pubkey: (0, bip371_js_1.toXOnly)(keyPair.publicKey),
+ signature: (0, bip371_js_1.serializeTaprootSignature)(
+ keyPair.signSchnorr(h.hash),
+ input.sighashType,
+ ),
+ leafHash: h.leafHash,
+ }));
+ if (tapKeySig) {
+ this.data.updateInput(inputIndex, { tapKeySig });
+ }
+ if (tapScriptSig.length) {
+ this.data.updateInput(inputIndex, { tapScriptSig });
+ }
+ return this;
+ }
+ signInputAsync(inputIndex, keyPair, sighashTypes) {
+ return Promise.resolve().then(() => {
+ if (!keyPair || !keyPair.publicKey)
+ throw new Error('Need Signer to sign input');
+ const input = (0, bip174_2.checkForInput)(this.data.inputs, inputIndex);
+ if ((0, bip371_js_1.isTaprootInput)(input))
+ return this._signTaprootInputAsync(
+ inputIndex,
+ input,
+ keyPair,
+ undefined,
+ sighashTypes,
+ );
+ return this._signInputAsync(inputIndex, keyPair, sighashTypes);
+ });
+ }
+ signTaprootInputAsync(inputIndex, keyPair, tapLeafHash, sighashTypes) {
+ return Promise.resolve().then(() => {
+ if (!keyPair || !keyPair.publicKey)
+ throw new Error('Need Signer to sign input');
+ const input = (0, bip174_2.checkForInput)(this.data.inputs, inputIndex);
+ if ((0, bip371_js_1.isTaprootInput)(input))
+ return this._signTaprootInputAsync(
+ inputIndex,
+ input,
+ keyPair,
+ tapLeafHash,
+ sighashTypes,
+ );
+ throw new Error(`Input #${inputIndex} is not of type Taproot.`);
+ });
+ }
+ _signInputAsync(
+ inputIndex,
+ keyPair,
+ sighashTypes = [transaction_js_1.Transaction.SIGHASH_ALL],
+ ) {
+ const { hash, sighashType } = getHashAndSighashType(
+ this.data.inputs,
+ inputIndex,
+ keyPair.publicKey,
+ this.__CACHE,
+ sighashTypes,
+ );
+ return Promise.resolve(keyPair.sign(hash)).then(signature => {
+ const partialSig = [
+ {
+ pubkey: keyPair.publicKey,
+ signature: bscript.signature.encode(signature, sighashType),
+ },
+ ];
+ this.data.updateInput(inputIndex, { partialSig });
+ });
+ }
+ async _signTaprootInputAsync(
+ inputIndex,
+ input,
+ keyPair,
+ tapLeafHash,
+ sighashTypes = [transaction_js_1.Transaction.SIGHASH_DEFAULT],
+ ) {
+ const hashesForSig = this.checkTaprootHashesForSig(
+ inputIndex,
+ input,
+ keyPair,
+ tapLeafHash,
+ sighashTypes,
+ );
+ const signaturePromises = [];
+ const tapKeyHash = hashesForSig.filter(h => !h.leafHash)[0];
+ if (tapKeyHash) {
+ const tapKeySigPromise = Promise.resolve(
+ keyPair.signSchnorr(tapKeyHash.hash),
+ ).then(sig => {
+ return {
+ tapKeySig: (0, bip371_js_1.serializeTaprootSignature)(
+ sig,
+ input.sighashType,
+ ),
+ };
+ });
+ signaturePromises.push(tapKeySigPromise);
+ }
+ const tapScriptHashes = hashesForSig.filter(h => !!h.leafHash);
+ if (tapScriptHashes.length) {
+ const tapScriptSigPromises = tapScriptHashes.map(tsh => {
+ return Promise.resolve(keyPair.signSchnorr(tsh.hash)).then(
+ signature => {
+ const tapScriptSig = [
+ {
+ pubkey: (0, bip371_js_1.toXOnly)(keyPair.publicKey),
+ signature: (0, bip371_js_1.serializeTaprootSignature)(
+ signature,
+ input.sighashType,
+ ),
+ leafHash: tsh.leafHash,
+ },
+ ];
+ return { tapScriptSig };
+ },
+ );
+ });
+ signaturePromises.push(...tapScriptSigPromises);
+ }
+ return Promise.all(signaturePromises).then(results => {
+ results.forEach(v => this.data.updateInput(inputIndex, v));
+ });
+ }
+ checkTaprootHashesForSig(
+ inputIndex,
+ input,
+ keyPair,
+ tapLeafHashToSign,
+ allowedSighashTypes,
+ ) {
+ if (typeof keyPair.signSchnorr !== 'function')
+ throw new Error(
+ `Need Schnorr Signer to sign taproot input #${inputIndex}.`,
+ );
+ const hashesForSig = getTaprootHashesForSigning(
+ inputIndex,
+ input,
+ this.data.inputs,
+ keyPair.publicKey,
+ this.__CACHE,
+ tapLeafHashToSign,
+ allowedSighashTypes,
+ );
+ if (!hashesForSig || !hashesForSig.length)
+ throw new Error(
+ `Can not sign for input #${inputIndex} with the key ${tools.toHex(keyPair.publicKey)}`,
+ );
+ return hashesForSig;
+ }
+ toBuffer() {
+ checkCache(this.__CACHE);
+ return this.data.toBuffer();
+ }
+ toHex() {
+ checkCache(this.__CACHE);
+ return this.data.toHex();
+ }
+ toBase64() {
+ checkCache(this.__CACHE);
+ return this.data.toBase64();
+ }
+ updateGlobal(updateData) {
+ this.data.updateGlobal(updateData);
+ return this;
+ }
+ updateInput(inputIndex, updateData) {
+ if (updateData.witnessScript) checkInvalidP2WSH(updateData.witnessScript);
+ (0, bip371_js_1.checkTaprootInputFields)(
+ this.data.inputs[inputIndex],
+ updateData,
+ 'updateInput',
+ );
+ this.data.updateInput(inputIndex, updateData);
+ if (updateData.nonWitnessUtxo) {
+ addNonWitnessTxCache(
+ this.__CACHE,
+ this.data.inputs[inputIndex],
+ inputIndex,
+ );
+ }
+ return this;
+ }
+ updateOutput(outputIndex, updateData) {
+ const outputData = this.data.outputs[outputIndex];
+ (0, bip371_js_1.checkTaprootOutputFields)(
+ outputData,
+ updateData,
+ 'updateOutput',
+ );
+ this.data.updateOutput(outputIndex, updateData);
+ return this;
+ }
+ addUnknownKeyValToGlobal(keyVal) {
+ this.data.addUnknownKeyValToGlobal(keyVal);
+ return this;
+ }
+ addUnknownKeyValToInput(inputIndex, keyVal) {
+ this.data.addUnknownKeyValToInput(inputIndex, keyVal);
+ return this;
+ }
+ addUnknownKeyValToOutput(outputIndex, keyVal) {
+ this.data.addUnknownKeyValToOutput(outputIndex, keyVal);
+ return this;
+ }
+ clearFinalizedInput(inputIndex) {
+ this.data.clearFinalizedInput(inputIndex);
+ return this;
+ }
+}
+exports.Psbt = Psbt;
+/**
+ * This function is needed to pass to the bip174 base class's fromBuffer.
+ * It takes the "transaction buffer" portion of the psbt buffer and returns a
+ * Transaction (From the bip174 library) interface.
+ */
+const transactionFromBuffer = buffer => new PsbtTransaction(buffer);
+/**
+ * This class implements the Transaction interface from bip174 library.
+ * It contains a bitcoinjs-lib Transaction object.
+ */
+class PsbtTransaction {
+ tx;
+ constructor(buffer = Uint8Array.from([2, 0, 0, 0, 0, 0, 0, 0, 0, 0])) {
+ this.tx = transaction_js_1.Transaction.fromBuffer(buffer);
+ checkTxEmpty(this.tx);
+ Object.defineProperty(this, 'tx', {
+ enumerable: false,
+ writable: true,
+ });
+ }
+ getInputOutputCounts() {
+ return {
+ inputCount: this.tx.ins.length,
+ outputCount: this.tx.outs.length,
+ };
+ }
+ addInput(input) {
+ if (
+ input.hash === undefined ||
+ input.index === undefined ||
+ (!(input.hash instanceof Uint8Array) && typeof input.hash !== 'string') ||
+ typeof input.index !== 'number'
+ ) {
+ throw new Error('Error adding input.');
+ }
+ const hash =
+ typeof input.hash === 'string'
+ ? (0, bufferutils_js_1.reverseBuffer)(tools.fromHex(input.hash))
+ : input.hash;
+ this.tx.addInput(hash, input.index, input.sequence);
+ }
+ addOutput(output) {
+ if (
+ output.script === undefined ||
+ output.value === undefined ||
+ !(output.script instanceof Uint8Array) ||
+ typeof output.value !== 'bigint'
+ ) {
+ throw new Error('Error adding output.');
+ }
+ this.tx.addOutput(output.script, output.value);
+ }
+ toBuffer() {
+ return this.tx.toBuffer();
+ }
+}
+function canFinalize(input, script, scriptType) {
+ switch (scriptType) {
+ case 'pubkey':
+ case 'pubkeyhash':
+ case 'witnesspubkeyhash':
+ return hasSigs(1, input.partialSig);
+ case 'multisig':
+ const p2ms = payments.p2ms({ output: script });
+ return hasSigs(p2ms.m, input.partialSig, p2ms.pubkeys);
+ default:
+ return false;
+ }
+}
+function checkCache(cache) {
+ if (cache.__UNSAFE_SIGN_NONSEGWIT !== false) {
+ throw new Error('Not BIP174 compliant, can not export');
+ }
+}
+function hasSigs(neededSigs, partialSig, pubkeys) {
+ if (!partialSig) return false;
+ let sigs;
+ if (pubkeys) {
+ sigs = pubkeys
+ .map(pkey => {
+ const pubkey = compressPubkey(pkey);
+ return partialSig.find(
+ pSig => tools.compare(pSig.pubkey, pubkey) === 0,
+ );
+ })
+ .filter(v => !!v);
+ } else {
+ sigs = partialSig;
+ }
+ if (sigs.length > neededSigs) throw new Error('Too many signatures');
+ return sigs.length === neededSigs;
+}
+function isFinalized(input) {
+ return !!input.finalScriptSig || !!input.finalScriptWitness;
+}
+function bip32DerivationIsMine(root) {
+ return d => {
+ if (tools.compare(root.fingerprint, d.masterFingerprint)) return false;
+ if (tools.compare(root.derivePath(d.path).publicKey, d.pubkey))
+ return false;
+ return true;
+ };
+}
+function check32Bit(num) {
+ if (
+ typeof num !== 'number' ||
+ num !== Math.floor(num) ||
+ num > 0xffffffff ||
+ num < 0
+ ) {
+ throw new Error('Invalid 32 bit integer');
+ }
+}
+function checkFees(psbt, cache, opts) {
+ const feeRate = cache.__FEE_RATE || psbt.getFeeRate();
+ const vsize = cache.__EXTRACTED_TX.virtualSize();
+ const satoshis = feeRate * vsize;
+ if (feeRate >= opts.maximumFeeRate) {
+ throw new Error(
+ `Warning: You are paying around ${(satoshis / 1e8).toFixed(8)} in ` +
+ `fees, which is ${feeRate} satoshi per byte for a transaction ` +
+ `with a VSize of ${vsize} bytes (segwit counted as 0.25 byte per ` +
+ `byte). Use setMaximumFeeRate method to raise your threshold, or ` +
+ `pass true to the first arg of extractTransaction.`,
+ );
+ }
+}
+function checkInputsForPartialSig(inputs, action) {
+ inputs.forEach(input => {
+ const throws = (0, bip371_js_1.isTaprootInput)(input)
+ ? (0, bip371_js_1.checkTaprootInputForSigs)(input, action)
+ : (0, psbtutils_js_1.checkInputForSig)(input, action);
+ if (throws)
+ throw new Error('Can not modify transaction, signatures exist.');
+ });
+}
+function checkPartialSigSighashes(input) {
+ if (!input.sighashType || !input.partialSig) return;
+ const { partialSig, sighashType } = input;
+ partialSig.forEach(pSig => {
+ const { hashType } = bscript.signature.decode(pSig.signature);
+ if (sighashType !== hashType) {
+ throw new Error('Signature sighash does not match input sighash type');
+ }
+ });
+}
+function checkScriptForPubkey(pubkey, script, action) {
+ if (!(0, psbtutils_js_1.pubkeyInScript)(pubkey, script)) {
+ throw new Error(
+ `Can not ${action} for this input with the key ${tools.toHex(pubkey)}`,
+ );
+ }
+}
+function checkTxEmpty(tx) {
+ const isEmpty = tx.ins.every(
+ input =>
+ input.script &&
+ input.script.length === 0 &&
+ input.witness &&
+ input.witness.length === 0,
+ );
+ if (!isEmpty) {
+ throw new Error('Format Error: Transaction ScriptSigs are not empty');
+ }
+}
+function checkTxForDupeIns(tx, cache) {
+ tx.ins.forEach(input => {
+ checkTxInputCache(cache, input);
+ });
+}
+function checkTxInputCache(cache, input) {
+ const key =
+ tools.toHex(
+ (0, bufferutils_js_1.reverseBuffer)(Uint8Array.from(input.hash)),
+ ) +
+ ':' +
+ input.index;
+ if (cache.__TX_IN_CACHE[key]) throw new Error('Duplicate input detected.');
+ cache.__TX_IN_CACHE[key] = 1;
+}
+function scriptCheckerFactory(payment, paymentScriptName) {
+ return (inputIndex, scriptPubKey, redeemScript, ioType) => {
+ const redeemScriptOutput = payment({
+ redeem: { output: redeemScript },
+ }).output;
+ if (tools.compare(scriptPubKey, redeemScriptOutput)) {
+ throw new Error(
+ `${paymentScriptName} for ${ioType} #${inputIndex} doesn't match the scriptPubKey in the prevout`,
+ );
+ }
+ };
+}
+const checkRedeemScript = scriptCheckerFactory(payments.p2sh, 'Redeem script');
+const checkWitnessScript = scriptCheckerFactory(
+ payments.p2wsh,
+ 'Witness script',
+);
+function getTxCacheValue(key, name, inputs, c) {
+ if (!inputs.every(isFinalized))
+ throw new Error(`PSBT must be finalized to calculate ${name}`);
+ if (key === '__FEE_RATE' && c.__FEE_RATE) return c.__FEE_RATE;
+ if (key === '__FEE' && c.__FEE) return c.__FEE;
+ let tx;
+ let mustFinalize = true;
+ if (c.__EXTRACTED_TX) {
+ tx = c.__EXTRACTED_TX;
+ mustFinalize = false;
+ } else {
+ tx = c.__TX.clone();
+ }
+ inputFinalizeGetAmts(inputs, tx, c, mustFinalize);
+ if (key === '__FEE_RATE') return c.__FEE_RATE;
+ else if (key === '__FEE') return c.__FEE;
+}
+function getFinalScripts(inputIndex, input, script, isSegwit, isP2SH, isP2WSH) {
+ const scriptType = classifyScript(script);
+ if (!canFinalize(input, script, scriptType))
+ throw new Error(`Can not finalize input #${inputIndex}`);
+ return prepareFinalScripts(
+ script,
+ scriptType,
+ input.partialSig,
+ isSegwit,
+ isP2SH,
+ isP2WSH,
+ );
+}
+function prepareFinalScripts(
+ script,
+ scriptType,
+ partialSig,
+ isSegwit,
+ isP2SH,
+ isP2WSH,
+) {
+ let finalScriptSig;
+ let finalScriptWitness;
+ // Wow, the payments API is very handy
+ const payment = getPayment(script, scriptType, partialSig);
+ const p2wsh = !isP2WSH ? null : payments.p2wsh({ redeem: payment });
+ const p2sh = !isP2SH ? null : payments.p2sh({ redeem: p2wsh || payment });
+ if (isSegwit) {
+ if (p2wsh) {
+ finalScriptWitness = (0, psbtutils_js_1.witnessStackToScriptWitness)(
+ p2wsh.witness,
+ );
+ } else {
+ finalScriptWitness = (0, psbtutils_js_1.witnessStackToScriptWitness)(
+ payment.witness,
+ );
+ }
+ if (p2sh) {
+ finalScriptSig = p2sh.input;
+ }
+ } else {
+ if (p2sh) {
+ finalScriptSig = p2sh.input;
+ } else {
+ finalScriptSig = payment.input;
+ }
+ }
+ return {
+ finalScriptSig,
+ finalScriptWitness,
+ };
+}
+function getHashAndSighashType(
+ inputs,
+ inputIndex,
+ pubkey,
+ cache,
+ sighashTypes,
+) {
+ const input = (0, bip174_2.checkForInput)(inputs, inputIndex);
+ const { hash, sighashType, script } = getHashForSig(
+ inputIndex,
+ input,
+ cache,
+ false,
+ sighashTypes,
+ );
+ checkScriptForPubkey(pubkey, script, 'sign');
+ return {
+ hash,
+ sighashType,
+ };
+}
+function getHashForSig(inputIndex, input, cache, forValidate, sighashTypes) {
+ const unsignedTx = cache.__TX;
+ const sighashType =
+ input.sighashType || transaction_js_1.Transaction.SIGHASH_ALL;
+ checkSighashTypeAllowed(sighashType, sighashTypes);
+ let hash;
+ let prevout;
+ if (input.nonWitnessUtxo) {
+ const nonWitnessUtxoTx = nonWitnessUtxoTxFromCache(
+ cache,
+ input,
+ inputIndex,
+ );
+ const prevoutHash = unsignedTx.ins[inputIndex].hash;
+ const utxoHash = nonWitnessUtxoTx.getHash();
+ // If a non-witness UTXO is provided, its hash must match the hash specified in the prevout
+ if (tools.compare(prevoutHash, utxoHash) !== 0) {
+ throw new Error(
+ `Non-witness UTXO hash for input #${inputIndex} doesn't match the hash specified in the prevout`,
+ );
+ }
+ const prevoutIndex = unsignedTx.ins[inputIndex].index;
+ prevout = nonWitnessUtxoTx.outs[prevoutIndex];
+ } else if (input.witnessUtxo) {
+ prevout = input.witnessUtxo;
+ } else {
+ throw new Error('Need a Utxo input item for signing');
+ }
+ const { meaningfulScript, type } = getMeaningfulScript(
+ prevout.script,
+ inputIndex,
+ 'input',
+ input.redeemScript,
+ input.witnessScript,
+ );
+ if (['p2sh-p2wsh', 'p2wsh'].indexOf(type) >= 0) {
+ hash = unsignedTx.hashForWitnessV0(
+ inputIndex,
+ meaningfulScript,
+ prevout.value,
+ sighashType,
+ );
+ } else if ((0, psbtutils_js_1.isP2WPKH)(meaningfulScript)) {
+ // P2WPKH uses the P2PKH template for prevoutScript when signing
+ const signingScript = payments.p2pkh({
+ hash: meaningfulScript.slice(2),
+ }).output;
+ hash = unsignedTx.hashForWitnessV0(
+ inputIndex,
+ signingScript,
+ prevout.value,
+ sighashType,
+ );
+ } else {
+ // non-segwit
+ if (
+ input.nonWitnessUtxo === undefined &&
+ cache.__UNSAFE_SIGN_NONSEGWIT === false
+ )
+ throw new Error(
+ `Input #${inputIndex} has witnessUtxo but non-segwit script: ` +
+ `${tools.toHex(meaningfulScript)}`,
+ );
+ if (!forValidate && cache.__UNSAFE_SIGN_NONSEGWIT !== false)
+ console.warn(
+ 'Warning: Signing non-segwit inputs without the full parent transaction ' +
+ 'means there is a chance that a miner could feed you incorrect information ' +
+ "to trick you into paying large fees. This behavior is the same as Psbt's predecessor " +
+ '(TransactionBuilder - now removed) when signing non-segwit scripts. You are not ' +
+ 'able to export this Psbt with toBuffer|toBase64|toHex since it is not ' +
+ 'BIP174 compliant.\n*********************\nPROCEED WITH CAUTION!\n' +
+ '*********************',
+ );
+ hash = unsignedTx.hashForSignature(
+ inputIndex,
+ meaningfulScript,
+ sighashType,
+ );
+ }
+ return {
+ script: meaningfulScript,
+ sighashType,
+ hash,
+ };
+}
+function getAllTaprootHashesForSigValidation(inputIndex, input, inputs, cache) {
+ const allPublicKeys = [];
+ if (input.tapInternalKey) {
+ const key = getPrevoutTaprootKey(inputIndex, input, cache);
+ if (key) {
+ allPublicKeys.push(key);
+ }
+ }
+ if (input.tapScriptSig) {
+ const tapScriptPubkeys = input.tapScriptSig.map(tss => tss.pubkey);
+ allPublicKeys.push(...tapScriptPubkeys);
+ }
+ const allHashes = allPublicKeys.map(publicKey =>
+ getTaprootHashesForSigValidation(
+ inputIndex,
+ input,
+ inputs,
+ publicKey,
+ cache,
+ ),
+ );
+ return allHashes.flat();
+}
+function getPrevoutTaprootKey(inputIndex, input, cache) {
+ const { script } = getScriptAndAmountFromUtxo(inputIndex, input, cache);
+ return (0, psbtutils_js_1.isP2TR)(script) ? script.subarray(2, 34) : null;
+}
+function trimTaprootSig(signature) {
+ return signature.length === 64 ? signature : signature.subarray(0, 64);
+}
+function getTaprootHashesForSigning(
+ inputIndex,
+ input,
+ inputs,
+ pubkey,
+ cache,
+ tapLeafHashToSign,
+ allowedSighashTypes,
+) {
+ const sighashType =
+ input.sighashType || transaction_js_1.Transaction.SIGHASH_DEFAULT;
+ checkSighashTypeAllowed(sighashType, allowedSighashTypes);
+ const keySpend = Boolean(input.tapInternalKey && !tapLeafHashToSign);
+ return getTaprootHashesForSig(
+ inputIndex,
+ input,
+ inputs,
+ pubkey,
+ cache,
+ keySpend,
+ sighashType,
+ tapLeafHashToSign,
+ );
+}
+function getTaprootHashesForSigValidation(
+ inputIndex,
+ input,
+ inputs,
+ pubkey,
+ cache,
+) {
+ const sighashType =
+ input.sighashType || transaction_js_1.Transaction.SIGHASH_DEFAULT;
+ const keySpend = Boolean(input.tapKeySig);
+ return getTaprootHashesForSig(
+ inputIndex,
+ input,
+ inputs,
+ pubkey,
+ cache,
+ keySpend,
+ sighashType,
+ );
+}
+/*
+ * This helper method is used for both generating a hash for signing as well for validating;
+ * thus depending on context key spend can be detected either with tapInternalKey with no leaves
+ * or tapKeySig (note tapKeySig is a signature to the prevout pk, so tapInternalKey is not needed).
+ */
+function getTaprootHashesForSig(
+ inputIndex,
+ input,
+ inputs,
+ pubkey,
+ cache,
+ keySpend,
+ sighashType,
+ tapLeafHashToSign,
+) {
+ const unsignedTx = cache.__TX;
+ const prevOuts = inputs.map((i, index) =>
+ getScriptAndAmountFromUtxo(index, i, cache),
+ );
+ const signingScripts = prevOuts.map(o => o.script);
+ const values = prevOuts.map(o => o.value);
+ const hashes = [];
+ if (keySpend) {
+ const outputKey =
+ getPrevoutTaprootKey(inputIndex, input, cache) || Uint8Array.from([]);
+ if (tools.compare((0, bip371_js_1.toXOnly)(pubkey), outputKey) === 0) {
+ const tapKeyHash = unsignedTx.hashForWitnessV1(
+ inputIndex,
+ signingScripts,
+ values,
+ sighashType,
+ );
+ hashes.push({ pubkey, hash: tapKeyHash });
+ }
+ }
+ const tapLeafHashes = (input.tapLeafScript || [])
+ .filter(tapLeaf =>
+ (0, psbtutils_js_1.pubkeyInScript)(pubkey, tapLeaf.script),
+ )
+ .map(tapLeaf => {
+ const hash = (0, bip341_js_1.tapleafHash)({
+ output: tapLeaf.script,
+ version: tapLeaf.leafVersion,
+ });
+ return Object.assign({ hash }, tapLeaf);
+ })
+ .filter(
+ tapLeaf =>
+ !tapLeafHashToSign ||
+ tools.compare(tapLeafHashToSign, tapLeaf.hash) === 0,
+ )
+ .map(tapLeaf => {
+ const tapScriptHash = unsignedTx.hashForWitnessV1(
+ inputIndex,
+ signingScripts,
+ values,
+ sighashType,
+ tapLeaf.hash,
+ );
+ return {
+ pubkey,
+ hash: tapScriptHash,
+ leafHash: tapLeaf.hash,
+ };
+ });
+ return hashes.concat(tapLeafHashes);
+}
+function checkSighashTypeAllowed(sighashType, sighashTypes) {
+ if (sighashTypes && sighashTypes.indexOf(sighashType) < 0) {
+ const str = sighashTypeToString(sighashType);
+ throw new Error(
+ `Sighash type is not allowed. Retry the sign method passing the ` +
+ `sighashTypes array of whitelisted types. Sighash type: ${str}`,
+ );
+ }
+}
+function getPayment(script, scriptType, partialSig) {
+ let payment;
+ switch (scriptType) {
+ case 'multisig':
+ const sigs = getSortedSigs(script, partialSig);
+ payment = payments.p2ms({
+ output: script,
+ signatures: sigs,
+ });
+ break;
+ case 'pubkey':
+ payment = payments.p2pk({
+ output: script,
+ signature: partialSig[0].signature,
+ });
+ break;
+ case 'pubkeyhash':
+ payment = payments.p2pkh({
+ output: script,
+ pubkey: partialSig[0].pubkey,
+ signature: partialSig[0].signature,
+ });
+ break;
+ case 'witnesspubkeyhash':
+ payment = payments.p2wpkh({
+ output: script,
+ pubkey: partialSig[0].pubkey,
+ signature: partialSig[0].signature,
+ });
+ break;
+ }
+ return payment;
+}
+function getScriptFromInput(inputIndex, input, cache) {
+ const unsignedTx = cache.__TX;
+ const res = {
+ script: null,
+ isSegwit: false,
+ isP2SH: false,
+ isP2WSH: false,
+ };
+ res.isP2SH = !!input.redeemScript;
+ res.isP2WSH = !!input.witnessScript;
+ if (input.witnessScript) {
+ res.script = input.witnessScript;
+ } else if (input.redeemScript) {
+ res.script = input.redeemScript;
+ } else {
+ if (input.nonWitnessUtxo) {
+ const nonWitnessUtxoTx = nonWitnessUtxoTxFromCache(
+ cache,
+ input,
+ inputIndex,
+ );
+ const prevoutIndex = unsignedTx.ins[inputIndex].index;
+ res.script = nonWitnessUtxoTx.outs[prevoutIndex].script;
+ } else if (input.witnessUtxo) {
+ res.script = input.witnessUtxo.script;
+ }
+ }
+ if (input.witnessScript || (0, psbtutils_js_1.isP2WPKH)(res.script)) {
+ res.isSegwit = true;
+ }
+ return res;
+}
+function getSignersFromHD(inputIndex, inputs, hdKeyPair) {
+ const input = (0, bip174_2.checkForInput)(inputs, inputIndex);
+ if (!input.bip32Derivation || input.bip32Derivation.length === 0) {
+ throw new Error('Need bip32Derivation to sign with HD');
+ }
+ const myDerivations = input.bip32Derivation
+ .map(bipDv => {
+ if (tools.compare(bipDv.masterFingerprint, hdKeyPair.fingerprint) === 0) {
+ return bipDv;
+ } else {
+ return;
+ }
+ })
+ .filter(v => !!v);
+ if (myDerivations.length === 0) {
+ throw new Error(
+ 'Need one bip32Derivation masterFingerprint to match the HDSigner fingerprint',
+ );
+ }
+ const signers = myDerivations.map(bipDv => {
+ const node = hdKeyPair.derivePath(bipDv.path);
+ if (tools.compare(bipDv.pubkey, node.publicKey) !== 0) {
+ throw new Error('pubkey did not match bip32Derivation');
+ }
+ return node;
+ });
+ return signers;
+}
+function getSortedSigs(script, partialSig) {
+ const p2ms = payments.p2ms({ output: script });
+ // for each pubkey in order of p2ms script
+ return p2ms.pubkeys
+ .map(pk => {
+ // filter partialSig array by pubkey being equal
+ return (
+ partialSig.filter(ps => {
+ return tools.compare(ps.pubkey, pk) === 0;
+ })[0] || {}
+ ).signature;
+ // Any pubkey without a match will return undefined
+ // this last filter removes all the undefined items in the array.
+ })
+ .filter(v => !!v);
+}
+function scriptWitnessToWitnessStack(buffer) {
+ let offset = 0;
+ function readSlice(n) {
+ offset += n;
+ return buffer.slice(offset - n, offset);
+ }
+ function readVarInt() {
+ const vi = varuint.decode(buffer, offset);
+ offset += varuint.encodingLength(vi.bigintValue);
+ return vi.numberValue;
+ }
+ function readVarSlice() {
+ return readSlice(readVarInt());
+ }
+ function readVector() {
+ const count = readVarInt();
+ const vector = [];
+ for (let i = 0; i < count; i++) vector.push(readVarSlice());
+ return vector;
+ }
+ return readVector();
+}
+function sighashTypeToString(sighashType) {
+ let text =
+ sighashType & transaction_js_1.Transaction.SIGHASH_ANYONECANPAY
+ ? 'SIGHASH_ANYONECANPAY | '
+ : '';
+ const sigMod = sighashType & 0x1f;
+ switch (sigMod) {
+ case transaction_js_1.Transaction.SIGHASH_ALL:
+ text += 'SIGHASH_ALL';
+ break;
+ case transaction_js_1.Transaction.SIGHASH_SINGLE:
+ text += 'SIGHASH_SINGLE';
+ break;
+ case transaction_js_1.Transaction.SIGHASH_NONE:
+ text += 'SIGHASH_NONE';
+ break;
+ }
+ return text;
+}
+function addNonWitnessTxCache(cache, input, inputIndex) {
+ cache.__NON_WITNESS_UTXO_BUF_CACHE[inputIndex] = input.nonWitnessUtxo;
+ const tx = transaction_js_1.Transaction.fromBuffer(input.nonWitnessUtxo);
+ cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex] = tx;
+ const self = cache;
+ const selfIndex = inputIndex;
+ delete input.nonWitnessUtxo;
+ Object.defineProperty(input, 'nonWitnessUtxo', {
+ enumerable: true,
+ get() {
+ const buf = self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex];
+ const txCache = self.__NON_WITNESS_UTXO_TX_CACHE[selfIndex];
+ if (buf !== undefined) {
+ return buf;
+ } else {
+ const newBuf = txCache.toBuffer();
+ self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex] = newBuf;
+ return newBuf;
+ }
+ },
+ set(data) {
+ self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex] = data;
+ },
+ });
+}
+function inputFinalizeGetAmts(inputs, tx, cache, mustFinalize) {
+ let inputAmount = 0n;
+ inputs.forEach((input, idx) => {
+ if (mustFinalize && input.finalScriptSig)
+ tx.ins[idx].script = input.finalScriptSig;
+ if (mustFinalize && input.finalScriptWitness) {
+ tx.ins[idx].witness = scriptWitnessToWitnessStack(
+ input.finalScriptWitness,
+ );
+ }
+ if (input.witnessUtxo) {
+ inputAmount += input.witnessUtxo.value;
+ } else if (input.nonWitnessUtxo) {
+ const nwTx = nonWitnessUtxoTxFromCache(cache, input, idx);
+ const vout = tx.ins[idx].index;
+ const out = nwTx.outs[vout];
+ inputAmount += out.value;
+ }
+ });
+ const outputAmount = tx.outs.reduce((total, o) => total + o.value, 0n);
+ const fee = inputAmount - outputAmount;
+ if (fee < 0) {
+ throw new Error('Outputs are spending more than Inputs');
+ }
+ const bytes = tx.virtualSize();
+ cache.__FEE = fee;
+ cache.__EXTRACTED_TX = tx;
+ cache.__FEE_RATE = Math.floor(Number(fee / BigInt(bytes)));
+}
+function nonWitnessUtxoTxFromCache(cache, input, inputIndex) {
+ const c = cache.__NON_WITNESS_UTXO_TX_CACHE;
+ if (!c[inputIndex]) {
+ addNonWitnessTxCache(cache, input, inputIndex);
+ }
+ return c[inputIndex];
+}
+function getScriptFromUtxo(inputIndex, input, cache) {
+ const { script } = getScriptAndAmountFromUtxo(inputIndex, input, cache);
+ return script;
+}
+function getScriptAndAmountFromUtxo(inputIndex, input, cache) {
+ if (input.witnessUtxo !== undefined) {
+ return {
+ script: input.witnessUtxo.script,
+ value: input.witnessUtxo.value,
+ };
+ } else if (input.nonWitnessUtxo !== undefined) {
+ const nonWitnessUtxoTx = nonWitnessUtxoTxFromCache(
+ cache,
+ input,
+ inputIndex,
+ );
+ const o = nonWitnessUtxoTx.outs[cache.__TX.ins[inputIndex].index];
+ return { script: o.script, value: o.value };
+ } else {
+ throw new Error("Can't find pubkey in input without Utxo data");
+ }
+}
+function pubkeyInInput(pubkey, input, inputIndex, cache) {
+ const script = getScriptFromUtxo(inputIndex, input, cache);
+ const { meaningfulScript } = getMeaningfulScript(
+ script,
+ inputIndex,
+ 'input',
+ input.redeemScript,
+ input.witnessScript,
+ );
+ return (0, psbtutils_js_1.pubkeyInScript)(pubkey, meaningfulScript);
+}
+function pubkeyInOutput(pubkey, output, outputIndex, cache) {
+ const script = cache.__TX.outs[outputIndex].script;
+ const { meaningfulScript } = getMeaningfulScript(
+ script,
+ outputIndex,
+ 'output',
+ output.redeemScript,
+ output.witnessScript,
+ );
+ return (0, psbtutils_js_1.pubkeyInScript)(pubkey, meaningfulScript);
+}
+function redeemFromFinalScriptSig(finalScript) {
+ if (!finalScript) return;
+ const decomp = bscript.decompile(finalScript);
+ if (!decomp) return;
+ const lastItem = decomp[decomp.length - 1];
+ if (
+ !(lastItem instanceof Uint8Array) ||
+ isPubkeyLike(lastItem) ||
+ isSigLike(lastItem)
+ )
+ return;
+ const sDecomp = bscript.decompile(lastItem);
+ if (!sDecomp) return;
+ return lastItem;
+}
+function redeemFromFinalWitnessScript(finalScript) {
+ if (!finalScript) return;
+ const decomp = scriptWitnessToWitnessStack(finalScript);
+ const lastItem = decomp[decomp.length - 1];
+ if (isPubkeyLike(lastItem)) return;
+ const sDecomp = bscript.decompile(lastItem);
+ if (!sDecomp) return;
+ return lastItem;
+}
+function compressPubkey(pubkey) {
+ if (pubkey.length === 65) {
+ const parity = pubkey[64] & 1;
+ const newKey = pubkey.slice(0, 33);
+ newKey[0] = 2 | parity;
+ return newKey;
+ }
+ return pubkey.slice();
+}
+function isPubkeyLike(buf) {
+ return buf.length === 33 && bscript.isCanonicalPubKey(buf);
+}
+function isSigLike(buf) {
+ return bscript.isCanonicalScriptSignature(buf);
+}
+function getMeaningfulScript(
+ script,
+ index,
+ ioType,
+ redeemScript,
+ witnessScript,
+) {
+ const isP2SH = (0, psbtutils_js_1.isP2SHScript)(script);
+ const isP2SHP2WSH =
+ isP2SH && redeemScript && (0, psbtutils_js_1.isP2WSHScript)(redeemScript);
+ const isP2WSH = (0, psbtutils_js_1.isP2WSHScript)(script);
+ if (isP2SH && redeemScript === undefined)
+ throw new Error('scriptPubkey is P2SH but redeemScript missing');
+ if ((isP2WSH || isP2SHP2WSH) && witnessScript === undefined)
+ throw new Error(
+ 'scriptPubkey or redeemScript is P2WSH but witnessScript missing',
+ );
+ let meaningfulScript;
+ if (isP2SHP2WSH) {
+ meaningfulScript = witnessScript;
+ checkRedeemScript(index, script, redeemScript, ioType);
+ checkWitnessScript(index, redeemScript, witnessScript, ioType);
+ checkInvalidP2WSH(meaningfulScript);
+ } else if (isP2WSH) {
+ meaningfulScript = witnessScript;
+ checkWitnessScript(index, script, witnessScript, ioType);
+ checkInvalidP2WSH(meaningfulScript);
+ } else if (isP2SH) {
+ meaningfulScript = redeemScript;
+ checkRedeemScript(index, script, redeemScript, ioType);
+ } else {
+ meaningfulScript = script;
+ }
+ return {
+ meaningfulScript,
+ type: isP2SHP2WSH
+ ? 'p2sh-p2wsh'
+ : isP2SH
+ ? 'p2sh'
+ : isP2WSH
+ ? 'p2wsh'
+ : 'raw',
+ };
+}
+function checkInvalidP2WSH(script) {
+ if (
+ (0, psbtutils_js_1.isP2WPKH)(script) ||
+ (0, psbtutils_js_1.isP2SHScript)(script)
+ ) {
+ throw new Error('P2WPKH or P2SH can not be contained within P2WSH');
+ }
+}
+function classifyScript(script) {
+ if ((0, psbtutils_js_1.isP2WPKH)(script)) return 'witnesspubkeyhash';
+ if ((0, psbtutils_js_1.isP2PKH)(script)) return 'pubkeyhash';
+ if ((0, psbtutils_js_1.isP2MS)(script)) return 'multisig';
+ if ((0, psbtutils_js_1.isP2PK)(script)) return 'pubkey';
+ return 'nonstandard';
+}
+function range(n) {
+ return [...Array(n).keys()];
+}
diff --git a/src/psbt.d.ts b/src/cjs/psbt.d.ts
similarity index 69%
rename from src/psbt.d.ts
rename to src/cjs/psbt.d.ts
index 8603a6955..92d65335d 100644
--- a/src/psbt.d.ts
+++ b/src/cjs/psbt.d.ts
@@ -1,29 +1,31 @@
-///
import { Psbt as PsbtBase } from 'bip174';
-import { KeyValue, PsbtGlobalUpdate, PsbtInput, PsbtInputUpdate, PsbtOutput, PsbtOutputUpdate } from 'bip174/src/lib/interfaces';
-import { Network } from './networks';
-import { Transaction } from './transaction';
+import { KeyValue, PsbtGlobalUpdate, PsbtInput, PsbtInputUpdate, PsbtOutput, PsbtOutputUpdate } from 'bip174';
+import { Network } from './networks.js';
+import { Transaction } from './transaction.js';
+import { toXOnly } from './psbt/bip371.js';
+export { toXOnly };
export interface TransactionInput {
- hash: string | Buffer;
+ hash: string | Uint8Array;
index: number;
sequence?: number;
}
export interface PsbtTxInput extends TransactionInput {
- hash: Buffer;
+ hash: Uint8Array;
}
export interface TransactionOutput {
- script: Buffer;
- value: number;
+ script: Uint8Array;
+ value: bigint;
}
export interface PsbtTxOutput extends TransactionOutput {
address: string | undefined;
}
-export declare type ValidateSigFunction = (pubkey: Buffer, msghash: Buffer, signature: Buffer) => boolean;
+export type ValidateSigFunction = (pubkey: Uint8Array, msghash: Uint8Array, signature: Uint8Array) => boolean;
/**
* Psbt class can parse and generate a PSBT binary based off of the BIP174.
* There are 6 roles that this class fulfills. (Explained in BIP174)
*
* Creator: This can be done with `new Psbt()`
+ *
* Updater: This can be done with `psbt.addInput(input)`, `psbt.addInputs(inputs)`,
* `psbt.addOutput(output)`, `psbt.addOutputs(outputs)` when you are looking to
* add new inputs and outputs to the PSBT, and `psbt.updateGlobal(itemObject)`,
@@ -34,20 +36,24 @@ export declare type ValidateSigFunction = (pubkey: Buffer, msghash: Buffer, sign
* data for updateOutput.
* For a list of what attributes should be what types. Check the bip174 library.
* Also, check the integration tests for some examples of usage.
+ *
* Signer: There are a few methods. signAllInputs and signAllInputsAsync, which will search all input
* information for your pubkey or pubkeyhash, and only sign inputs where it finds
* your info. Or you can explicitly sign a specific input with signInput and
* signInputAsync. For the async methods you can create a SignerAsync object
* and use something like a hardware wallet to sign with. (You must implement this)
+ *
* Combiner: psbts can be combined easily with `psbt.combine(psbt2, psbt3, psbt4 ...)`
* the psbt calling combine will always have precedence when a conflict occurs.
* Combine checks if the internal bitcoin transaction is the same, so be sure that
* all sequences, version, locktime, etc. are the same before combining.
+ *
* Input Finalizer: This role is fairly important. Not only does it need to construct
* the input scriptSigs and witnesses, but it SHOULD verify the signatures etc.
* Before running `psbt.finalizeAllInputs()` please run `psbt.validateSignaturesOfAllInputs()`
* Running any finalize method will delete any data in the input(s) that are no longer
* needed due to the finalized scripts containing the information.
+ *
* Transaction Extractor: This role will perform some checks before returning a
* Transaction object. Such as fee rate not being larger than maximumFeeRate etc.
*/
@@ -55,7 +61,7 @@ export declare class Psbt {
readonly data: PsbtBase;
static fromBase64(data: string, opts?: PsbtOptsOptional): Psbt;
static fromHex(data: string, opts?: PsbtOptsOptional): Psbt;
- static fromBuffer(buffer: Buffer, opts?: PsbtOptsOptional): Psbt;
+ static fromBuffer(buffer: Uint8Array, opts?: PsbtOptsOptional): Psbt;
private __CACHE;
private opts;
constructor(opts?: PsbtOptsOptional, data?: PsbtBase);
@@ -78,16 +84,21 @@ export declare class Psbt {
addOutput(outputData: PsbtOutputExtended): this;
extractTransaction(disableFeeCheck?: boolean): Transaction;
getFeeRate(): number;
- getFee(): number;
+ getFee(): bigint;
finalizeAllInputs(): this;
- finalizeInput(inputIndex: number, finalScriptsFunc?: FinalScriptsFunc): this;
+ finalizeInput(inputIndex: number, finalScriptsFunc?: FinalScriptsFunc | FinalTaprootScriptsFunc): this;
+ finalizeTaprootInput(inputIndex: number, tapLeafHashToFinalize?: Uint8Array, finalScriptsFunc?: FinalTaprootScriptsFunc): this;
+ private _finalizeInput;
+ private _finalizeTaprootInput;
getInputType(inputIndex: number): AllScriptType;
- inputHasPubkey(inputIndex: number, pubkey: Buffer): boolean;
+ inputHasPubkey(inputIndex: number, pubkey: Uint8Array): boolean;
inputHasHDKey(inputIndex: number, root: HDSigner): boolean;
- outputHasPubkey(outputIndex: number, pubkey: Buffer): boolean;
+ outputHasPubkey(outputIndex: number, pubkey: Uint8Array): boolean;
outputHasHDKey(outputIndex: number, root: HDSigner): boolean;
validateSignaturesOfAllInputs(validator: ValidateSigFunction): boolean;
- validateSignaturesOfInput(inputIndex: number, validator: ValidateSigFunction, pubkey?: Buffer): boolean;
+ validateSignaturesOfInput(inputIndex: number, validator: ValidateSigFunction, pubkey?: Uint8Array): boolean;
+ private _validateSignaturesOfInput;
+ private validateSignaturesOfTaprootInput;
signAllInputsHD(hdKeyPair: HDSigner, sighashTypes?: number[]): this;
signAllInputsHDAsync(hdKeyPair: HDSigner | HDSignerAsync, sighashTypes?: number[]): Promise;
signInputHD(inputIndex: number, hdKeyPair: HDSigner, sighashTypes?: number[]): this;
@@ -95,8 +106,15 @@ export declare class Psbt {
signAllInputs(keyPair: Signer, sighashTypes?: number[]): this;
signAllInputsAsync(keyPair: Signer | SignerAsync, sighashTypes?: number[]): Promise;
signInput(inputIndex: number, keyPair: Signer, sighashTypes?: number[]): this;
+ signTaprootInput(inputIndex: number, keyPair: Signer, tapLeafHashToSign?: Uint8Array, sighashTypes?: number[]): this;
+ private _signInput;
+ private _signTaprootInput;
signInputAsync(inputIndex: number, keyPair: Signer | SignerAsync, sighashTypes?: number[]): Promise;
- toBuffer(): Buffer;
+ signTaprootInputAsync(inputIndex: number, keyPair: Signer | SignerAsync, tapLeafHash?: Uint8Array, sighashTypes?: number[]): Promise;
+ private _signInputAsync;
+ private _signTaprootInputAsync;
+ private checkTaprootHashesForSig;
+ toBuffer(): Uint8Array;
toHex(): string;
toBase64(): string;
updateGlobal(updateData: PsbtGlobalUpdate): this;
@@ -113,24 +131,24 @@ interface PsbtOptsOptional {
}
interface PsbtInputExtended extends PsbtInput, TransactionInput {
}
-declare type PsbtOutputExtended = PsbtOutputExtendedAddress | PsbtOutputExtendedScript;
+type PsbtOutputExtended = PsbtOutputExtendedAddress | PsbtOutputExtendedScript;
interface PsbtOutputExtendedAddress extends PsbtOutput {
address: string;
- value: number;
+ value: bigint;
}
interface PsbtOutputExtendedScript extends PsbtOutput {
- script: Buffer;
- value: number;
+ script: Uint8Array;
+ value: bigint;
}
interface HDSignerBase {
/**
* DER format compressed publicKey buffer
*/
- publicKey: Buffer;
+ publicKey: Uint8Array;
/**
* The first 4 bytes of the sha256-ripemd160 of the publicKey
*/
- fingerprint: Buffer;
+ fingerprint: Uint8Array;
}
export interface HDSigner extends HDSignerBase {
/**
@@ -142,26 +160,28 @@ export interface HDSigner extends HDSignerBase {
* Input hash (the "message digest") for the signature algorithm
* Return a 64 byte signature (32 byte r and 32 byte s in that order)
*/
- sign(hash: Buffer): Buffer;
+ sign(hash: Uint8Array): Uint8Array;
}
/**
* Same as above but with async sign method
*/
export interface HDSignerAsync extends HDSignerBase {
derivePath(path: string): HDSignerAsync;
- sign(hash: Buffer): Promise;
+ sign(hash: Uint8Array): Promise;
}
export interface Signer {
- publicKey: Buffer;
+ publicKey: Uint8Array;
network?: any;
- sign(hash: Buffer, lowR?: boolean): Buffer;
- getPublicKey?(): Buffer;
+ sign(hash: Uint8Array, lowR?: boolean): Uint8Array;
+ signSchnorr?(hash: Uint8Array): Uint8Array;
+ getPublicKey?(): Uint8Array;
}
export interface SignerAsync {
- publicKey: Buffer;
+ publicKey: Uint8Array;
network?: any;
- sign(hash: Buffer, lowR?: boolean): Promise;
- getPublicKey?(): Buffer;
+ sign(hash: Uint8Array, lowR?: boolean): Promise;
+ signSchnorr?(hash: Uint8Array): Promise;
+ getPublicKey?(): Uint8Array;
}
/**
* This function must do two things:
@@ -169,14 +189,18 @@ export interface SignerAsync {
* ie. `Can not finalize input #${inputIndex}`
* 2. Create the finalScriptSig and finalScriptWitness Buffers.
*/
-declare type FinalScriptsFunc = (inputIndex: number, // Which input is it?
+type FinalScriptsFunc = (inputIndex: number, // Which input is it?
input: PsbtInput, // The PSBT input contents
-script: Buffer, // The "meaningful" locking script Buffer (redeemScript for P2SH etc.)
+script: Uint8Array, // The "meaningful" locking script Buffer (redeemScript for P2SH etc.)
isSegwit: boolean, // Is it segwit?
isP2SH: boolean, // Is it P2SH?
isP2WSH: boolean) => {
- finalScriptSig: Buffer | undefined;
- finalScriptWitness: Buffer | undefined;
+ finalScriptSig: Uint8Array | undefined;
+ finalScriptWitness: Uint8Array | undefined;
+};
+type FinalTaprootScriptsFunc = (inputIndex: number, // Which input is it?
+input: PsbtInput, // The PSBT input contents
+tapLeafHashToFinalize?: Uint8Array) => {
+ finalScriptWitness: Uint8Array | undefined;
};
-declare type AllScriptType = 'witnesspubkeyhash' | 'pubkeyhash' | 'multisig' | 'pubkey' | 'nonstandard' | 'p2sh-witnesspubkeyhash' | 'p2sh-pubkeyhash' | 'p2sh-multisig' | 'p2sh-pubkey' | 'p2sh-nonstandard' | 'p2wsh-pubkeyhash' | 'p2wsh-multisig' | 'p2wsh-pubkey' | 'p2wsh-nonstandard' | 'p2sh-p2wsh-pubkeyhash' | 'p2sh-p2wsh-multisig' | 'p2sh-p2wsh-pubkey' | 'p2sh-p2wsh-nonstandard';
-export {};
+type AllScriptType = 'witnesspubkeyhash' | 'pubkeyhash' | 'multisig' | 'pubkey' | 'nonstandard' | 'p2sh-witnesspubkeyhash' | 'p2sh-pubkeyhash' | 'p2sh-multisig' | 'p2sh-pubkey' | 'p2sh-nonstandard' | 'p2wsh-pubkeyhash' | 'p2wsh-multisig' | 'p2wsh-pubkey' | 'p2wsh-nonstandard' | 'p2sh-p2wsh-pubkeyhash' | 'p2sh-p2wsh-multisig' | 'p2sh-p2wsh-pubkey' | 'p2sh-p2wsh-nonstandard';
diff --git a/src/cjs/psbt/bip371.cjs b/src/cjs/psbt/bip371.cjs
new file mode 100644
index 000000000..55edda198
--- /dev/null
+++ b/src/cjs/psbt/bip371.cjs
@@ -0,0 +1,556 @@
+'use strict';
+var __createBinding =
+ (this && this.__createBinding) ||
+ (Object.create
+ ? function (o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ var desc = Object.getOwnPropertyDescriptor(m, k);
+ if (
+ !desc ||
+ ('get' in desc ? !m.__esModule : desc.writable || desc.configurable)
+ ) {
+ desc = {
+ enumerable: true,
+ get: function () {
+ return m[k];
+ },
+ };
+ }
+ Object.defineProperty(o, k2, desc);
+ }
+ : function (o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ o[k2] = m[k];
+ });
+var __setModuleDefault =
+ (this && this.__setModuleDefault) ||
+ (Object.create
+ ? function (o, v) {
+ Object.defineProperty(o, 'default', { enumerable: true, value: v });
+ }
+ : function (o, v) {
+ o['default'] = v;
+ });
+var __importStar =
+ (this && this.__importStar) ||
+ function (mod) {
+ if (mod && mod.__esModule) return mod;
+ var result = {};
+ if (mod != null)
+ for (var k in mod)
+ if (k !== 'default' && Object.prototype.hasOwnProperty.call(mod, k))
+ __createBinding(result, mod, k);
+ __setModuleDefault(result, mod);
+ return result;
+ };
+Object.defineProperty(exports, '__esModule', { value: true });
+exports.toXOnly = void 0;
+exports.tapScriptFinalizer = tapScriptFinalizer;
+exports.serializeTaprootSignature = serializeTaprootSignature;
+exports.isTaprootInput = isTaprootInput;
+exports.isTaprootOutput = isTaprootOutput;
+exports.checkTaprootInputFields = checkTaprootInputFields;
+exports.checkTaprootOutputFields = checkTaprootOutputFields;
+exports.tweakInternalPubKey = tweakInternalPubKey;
+exports.tapTreeToList = tapTreeToList;
+exports.tapTreeFromList = tapTreeFromList;
+exports.checkTaprootInputForSigs = checkTaprootInputForSigs;
+const types_js_1 = require('../types.cjs');
+const transaction_js_1 = require('../transaction.cjs');
+const psbtutils_js_1 = require('./psbtutils.cjs');
+const bip341_js_1 = require('../payments/bip341.cjs');
+const index_js_1 = require('../payments/index.cjs');
+const tools = __importStar(require('uint8array-tools'));
+const psbtutils_js_2 = require('./psbtutils.cjs');
+/**
+ * Converts a public key to an X-only public key.
+ * @param pubKey The public key to convert.
+ * @returns The X-only public key.
+ */
+const toXOnly = pubKey => (pubKey.length === 32 ? pubKey : pubKey.slice(1, 33));
+exports.toXOnly = toXOnly;
+/**
+ * Default tapscript finalizer. It searches for the `tapLeafHashToFinalize` if provided.
+ * Otherwise it will search for the tapleaf that has at least one signature and has the shortest path.
+ * @param inputIndex the position of the PSBT input.
+ * @param input the PSBT input.
+ * @param tapLeafHashToFinalize optional, if provided the finalizer will search for a tapleaf that has this hash
+ * and will try to build the finalScriptWitness.
+ * @returns the finalScriptWitness or throws an exception if no tapleaf found.
+ */
+function tapScriptFinalizer(inputIndex, input, tapLeafHashToFinalize) {
+ const tapLeaf = findTapLeafToFinalize(
+ input,
+ inputIndex,
+ tapLeafHashToFinalize,
+ );
+ try {
+ const sigs = sortSignatures(input, tapLeaf);
+ const witness = sigs.concat(tapLeaf.script).concat(tapLeaf.controlBlock);
+ return {
+ finalScriptWitness: (0, psbtutils_js_1.witnessStackToScriptWitness)(
+ witness,
+ ),
+ };
+ } catch (err) {
+ throw new Error(`Can not finalize taproot input #${inputIndex}: ${err}`);
+ }
+}
+/**
+ * Serializes a taproot signature.
+ * @param sig The signature to serialize.
+ * @param sighashType The sighash type. Optional.
+ * @returns The serialized taproot signature.
+ */
+function serializeTaprootSignature(sig, sighashType) {
+ const sighashTypeByte = sighashType
+ ? Uint8Array.from([sighashType])
+ : Uint8Array.from([]);
+ return tools.concat([sig, sighashTypeByte]);
+}
+/**
+ * Checks if a PSBT input is a taproot input.
+ * @param input The PSBT input to check.
+ * @returns True if the input is a taproot input, false otherwise.
+ */
+function isTaprootInput(input) {
+ return (
+ input &&
+ !!(
+ input.tapInternalKey ||
+ input.tapMerkleRoot ||
+ (input.tapLeafScript && input.tapLeafScript.length) ||
+ (input.tapBip32Derivation && input.tapBip32Derivation.length) ||
+ (input.witnessUtxo &&
+ (0, psbtutils_js_1.isP2TR)(input.witnessUtxo.script))
+ )
+ );
+}
+/**
+ * Checks if a PSBT output is a taproot output.
+ * @param output The PSBT output to check.
+ * @param script The script to check. Optional.
+ * @returns True if the output is a taproot output, false otherwise.
+ */
+function isTaprootOutput(output, script) {
+ return (
+ output &&
+ !!(
+ output.tapInternalKey ||
+ output.tapTree ||
+ (output.tapBip32Derivation && output.tapBip32Derivation.length) ||
+ (script && (0, psbtutils_js_1.isP2TR)(script))
+ )
+ );
+}
+/**
+ * Checks the taproot input fields for consistency.
+ * @param inputData The original input data.
+ * @param newInputData The new input data.
+ * @param action The action being performed.
+ * @throws Throws an error if the input fields are inconsistent.
+ */
+function checkTaprootInputFields(inputData, newInputData, action) {
+ checkMixedTaprootAndNonTaprootInputFields(inputData, newInputData, action);
+ checkIfTapLeafInTree(inputData, newInputData, action);
+}
+/**
+ * Checks the taproot output fields for consistency.
+ * @param outputData The original output data.
+ * @param newOutputData The new output data.
+ * @param action The action being performed.
+ * @throws Throws an error if the output fields are inconsistent.
+ */
+function checkTaprootOutputFields(outputData, newOutputData, action) {
+ checkMixedTaprootAndNonTaprootOutputFields(outputData, newOutputData, action);
+ checkTaprootScriptPubkey(outputData, newOutputData);
+}
+function checkTaprootScriptPubkey(outputData, newOutputData) {
+ if (!newOutputData.tapTree && !newOutputData.tapInternalKey) return;
+ const tapInternalKey =
+ newOutputData.tapInternalKey || outputData.tapInternalKey;
+ const tapTree = newOutputData.tapTree || outputData.tapTree;
+ if (tapInternalKey) {
+ const { script: scriptPubkey } = outputData;
+ const script = getTaprootScripPubkey(tapInternalKey, tapTree);
+ if (scriptPubkey && tools.compare(script, scriptPubkey) !== 0)
+ throw new Error('Error adding output. Script or address mismatch.');
+ }
+}
+/**
+ * Returns the Taproot script public key.
+ *
+ * @param tapInternalKey - The Taproot internal key.
+ * @param tapTree - The Taproot tree (optional).
+ * @returns The Taproot script public key.
+ */
+function getTaprootScripPubkey(tapInternalKey, tapTree) {
+ const scriptTree = tapTree && tapTreeFromList(tapTree.leaves);
+ const { output } = (0, index_js_1.p2tr)({
+ internalPubkey: tapInternalKey,
+ scriptTree,
+ });
+ return output;
+}
+/**
+ * Tweak the internal public key for a specific input.
+ * @param inputIndex - The index of the input.
+ * @param input - The PsbtInput object representing the input.
+ * @returns The tweaked internal public key.
+ * @throws Error if the tap internal key cannot be tweaked.
+ */
+function tweakInternalPubKey(inputIndex, input) {
+ const tapInternalKey = input.tapInternalKey;
+ const outputKey =
+ tapInternalKey &&
+ (0, bip341_js_1.tweakKey)(tapInternalKey, input.tapMerkleRoot);
+ if (!outputKey)
+ throw new Error(
+ `Cannot tweak tap internal key for input #${inputIndex}. Public key: ${
+ // tapInternalKey && tapInternalKey.toString('hex')
+ tapInternalKey && tools.toHex(tapInternalKey)
+ }`,
+ );
+ return outputKey.x;
+}
+/**
+ * Convert a binary tree to a BIP371 type list. Each element of the list is (according to BIP371):
+ * One or more tuples representing the depth, leaf version, and script for a leaf in the Taproot tree,
+ * allowing the entire tree to be reconstructed. The tuples must be in depth first search order so that
+ * the tree is correctly reconstructed.
+ * @param tree the binary tap tree
+ * @returns a list of BIP 371 tapleaves
+ */
+function tapTreeToList(tree) {
+ if (!(0, types_js_1.isTaptree)(tree))
+ throw new Error(
+ 'Cannot convert taptree to tapleaf list. Expecting a tapree structure.',
+ );
+ return _tapTreeToList(tree);
+}
+/**
+ * Convert a BIP371 TapLeaf list to a TapTree (binary).
+ * @param leaves a list of tapleaves where each element of the list is (according to BIP371):
+ * One or more tuples representing the depth, leaf version, and script for a leaf in the Taproot tree,
+ * allowing the entire tree to be reconstructed. The tuples must be in depth first search order so that
+ * the tree is correctly reconstructed.
+ * @returns the corresponding taptree, or throws an exception if the tree cannot be reconstructed
+ */
+function tapTreeFromList(leaves = []) {
+ if (leaves.length === 1 && leaves[0].depth === 0)
+ return {
+ output: leaves[0].script,
+ version: leaves[0].leafVersion,
+ };
+ return instertLeavesInTree(leaves);
+}
+/**
+ * Checks the taproot input for signatures.
+ * @param input The PSBT input to check.
+ * @param action The action being performed.
+ * @returns True if the input has taproot signatures, false otherwise.
+ */
+function checkTaprootInputForSigs(input, action) {
+ const sigs = extractTaprootSigs(input);
+ return sigs.some(sig =>
+ (0, psbtutils_js_2.signatureBlocksAction)(
+ sig,
+ decodeSchnorrSignature,
+ action,
+ ),
+ );
+}
+/**
+ * Decodes a Schnorr signature.
+ * @param signature The signature to decode.
+ * @returns The decoded Schnorr signature.
+ */
+function decodeSchnorrSignature(signature) {
+ return {
+ signature: signature.slice(0, 64),
+ hashType:
+ signature.slice(64)[0] || transaction_js_1.Transaction.SIGHASH_DEFAULT,
+ };
+}
+/**
+ * Extracts taproot signatures from a PSBT input.
+ * @param input The PSBT input to extract signatures from.
+ * @returns An array of taproot signatures.
+ */
+function extractTaprootSigs(input) {
+ const sigs = [];
+ if (input.tapKeySig) sigs.push(input.tapKeySig);
+ if (input.tapScriptSig)
+ sigs.push(...input.tapScriptSig.map(s => s.signature));
+ if (!sigs.length) {
+ const finalTapKeySig = getTapKeySigFromWitness(input.finalScriptWitness);
+ if (finalTapKeySig) sigs.push(finalTapKeySig);
+ }
+ return sigs;
+}
+/**
+ * Gets the taproot signature from the witness.
+ * @param finalScriptWitness The final script witness.
+ * @returns The taproot signature, or undefined if not found.
+ */
+function getTapKeySigFromWitness(finalScriptWitness) {
+ if (!finalScriptWitness) return;
+ const witness = finalScriptWitness.slice(2);
+ // todo: add schnorr signature validation
+ if (witness.length === 64 || witness.length === 65) return witness;
+}
+/**
+ * Converts a binary tree to a BIP371 type list.
+ * @param tree The binary tap tree.
+ * @param leaves A list of tapleaves. Optional.
+ * @param depth The current depth. Optional.
+ * @returns A list of BIP 371 tapleaves.
+ * @throws Throws an error if the taptree cannot be converted to a tapleaf list.
+ */
+function _tapTreeToList(tree, leaves = [], depth = 0) {
+ if (depth > bip341_js_1.MAX_TAPTREE_DEPTH)
+ throw new Error('Max taptree depth exceeded.');
+ if (!tree) return [];
+ if ((0, types_js_1.isTapleaf)(tree)) {
+ leaves.push({
+ depth,
+ leafVersion: tree.version || bip341_js_1.LEAF_VERSION_TAPSCRIPT,
+ script: tree.output,
+ });
+ return leaves;
+ }
+ if (tree[0]) _tapTreeToList(tree[0], leaves, depth + 1);
+ if (tree[1]) _tapTreeToList(tree[1], leaves, depth + 1);
+ return leaves;
+}
+/**
+ * Inserts the tapleaves into the taproot tree.
+ * @param leaves The tapleaves to insert.
+ * @returns The taproot tree.
+ * @throws Throws an error if there is no room left to insert a tapleaf in the tree.
+ */
+function instertLeavesInTree(leaves) {
+ let tree;
+ for (const leaf of leaves) {
+ tree = instertLeafInTree(leaf, tree);
+ if (!tree) throw new Error(`No room left to insert tapleaf in tree`);
+ }
+ return tree;
+}
+/**
+ * Inserts a tapleaf into the taproot tree.
+ * @param leaf The tapleaf to insert.
+ * @param tree The taproot tree.
+ * @param depth The current depth. Optional.
+ * @returns The updated taproot tree.
+ */
+function instertLeafInTree(leaf, tree, depth = 0) {
+ if (depth > bip341_js_1.MAX_TAPTREE_DEPTH)
+ throw new Error('Max taptree depth exceeded.');
+ if (leaf.depth === depth) {
+ if (!tree)
+ return {
+ output: leaf.script,
+ version: leaf.leafVersion,
+ };
+ return;
+ }
+ if ((0, types_js_1.isTapleaf)(tree)) return;
+ const leftSide = instertLeafInTree(leaf, tree && tree[0], depth + 1);
+ if (leftSide) return [leftSide, tree && tree[1]];
+ const rightSide = instertLeafInTree(leaf, tree && tree[1], depth + 1);
+ if (rightSide) return [tree && tree[0], rightSide];
+}
+/**
+ * Checks the input fields for mixed taproot and non-taproot fields.
+ * @param inputData The original input data.
+ * @param newInputData The new input data.
+ * @param action The action being performed.
+ * @throws Throws an error if the input fields are inconsistent.
+ */
+function checkMixedTaprootAndNonTaprootInputFields(
+ inputData,
+ newInputData,
+ action,
+) {
+ const isBadTaprootUpdate =
+ isTaprootInput(inputData) && hasNonTaprootFields(newInputData);
+ const isBadNonTaprootUpdate =
+ hasNonTaprootFields(inputData) && isTaprootInput(newInputData);
+ const hasMixedFields =
+ inputData === newInputData &&
+ isTaprootInput(newInputData) &&
+ hasNonTaprootFields(newInputData); // todo: bad? use !===
+ if (isBadTaprootUpdate || isBadNonTaprootUpdate || hasMixedFields)
+ throw new Error(
+ `Invalid arguments for Psbt.${action}. ` +
+ `Cannot use both taproot and non-taproot fields.`,
+ );
+}
+/**
+ * Checks the output fields for mixed taproot and non-taproot fields.
+ * @param inputData The original output data.
+ * @param newInputData The new output data.
+ * @param action The action being performed.
+ * @throws Throws an error if the output fields are inconsistent.
+ */
+function checkMixedTaprootAndNonTaprootOutputFields(
+ inputData,
+ newInputData,
+ action,
+) {
+ const isBadTaprootUpdate =
+ isTaprootOutput(inputData) && hasNonTaprootFields(newInputData);
+ const isBadNonTaprootUpdate =
+ hasNonTaprootFields(inputData) && isTaprootOutput(newInputData);
+ const hasMixedFields =
+ inputData === newInputData &&
+ isTaprootOutput(newInputData) &&
+ hasNonTaprootFields(newInputData);
+ if (isBadTaprootUpdate || isBadNonTaprootUpdate || hasMixedFields)
+ throw new Error(
+ `Invalid arguments for Psbt.${action}. ` +
+ `Cannot use both taproot and non-taproot fields.`,
+ );
+}
+/**
+ * Checks if the tap leaf is part of the tap tree for the given input data.
+ * Throws an error if the tap leaf is not part of the tap tree.
+ * @param inputData - The original PsbtInput data.
+ * @param newInputData - The new PsbtInput data.
+ * @param action - The action being performed.
+ * @throws {Error} - If the tap leaf is not part of the tap tree.
+ */
+function checkIfTapLeafInTree(inputData, newInputData, action) {
+ if (newInputData.tapMerkleRoot) {
+ const newLeafsInTree = (newInputData.tapLeafScript || []).every(l =>
+ isTapLeafInTree(l, newInputData.tapMerkleRoot),
+ );
+ const oldLeafsInTree = (inputData.tapLeafScript || []).every(l =>
+ isTapLeafInTree(l, newInputData.tapMerkleRoot),
+ );
+ if (!newLeafsInTree || !oldLeafsInTree)
+ throw new Error(
+ `Invalid arguments for Psbt.${action}. Tapleaf not part of taptree.`,
+ );
+ } else if (inputData.tapMerkleRoot) {
+ const newLeafsInTree = (newInputData.tapLeafScript || []).every(l =>
+ isTapLeafInTree(l, inputData.tapMerkleRoot),
+ );
+ if (!newLeafsInTree)
+ throw new Error(
+ `Invalid arguments for Psbt.${action}. Tapleaf not part of taptree.`,
+ );
+ }
+}
+/**
+ * Checks if a TapLeafScript is present in a Merkle tree.
+ * @param tapLeaf The TapLeafScript to check.
+ * @param merkleRoot The Merkle root of the tree. If not provided, the function assumes the TapLeafScript is present.
+ * @returns A boolean indicating whether the TapLeafScript is present in the tree.
+ */
+function isTapLeafInTree(tapLeaf, merkleRoot) {
+ if (!merkleRoot) return true;
+ const leafHash = (0, bip341_js_1.tapleafHash)({
+ output: tapLeaf.script,
+ version: tapLeaf.leafVersion,
+ });
+ const rootHash = (0, bip341_js_1.rootHashFromPath)(
+ tapLeaf.controlBlock,
+ leafHash,
+ );
+ return tools.compare(rootHash, merkleRoot) === 0;
+}
+/**
+ * Sorts the signatures in the input's tapScriptSig array based on their position in the tapLeaf script.
+ *
+ * @param input - The PsbtInput object.
+ * @param tapLeaf - The TapLeafScript object.
+ * @returns An array of sorted signatures as Buffers.
+ */
+function sortSignatures(input, tapLeaf) {
+ const leafHash = (0, bip341_js_1.tapleafHash)({
+ output: tapLeaf.script,
+ version: tapLeaf.leafVersion,
+ });
+ return (
+ (input.tapScriptSig || [])
+ // .filter(tss => tss.leafHash.equals(leafHash))
+ .filter(tss => tools.compare(tss.leafHash, leafHash) === 0)
+ .map(tss => addPubkeyPositionInScript(tapLeaf.script, tss))
+ .sort((t1, t2) => t2.positionInScript - t1.positionInScript)
+ .map(t => t.signature)
+ );
+}
+/**
+ * Adds the position of a public key in a script to a TapScriptSig object.
+ * @param script The script in which to find the position of the public key.
+ * @param tss The TapScriptSig object to add the position to.
+ * @returns A TapScriptSigWitPosition object with the added position.
+ */
+function addPubkeyPositionInScript(script, tss) {
+ return Object.assign(
+ {
+ positionInScript: (0, psbtutils_js_1.pubkeyPositionInScript)(
+ tss.pubkey,
+ script,
+ ),
+ },
+ tss,
+ );
+}
+/**
+ * Find tapleaf by hash, or get the signed tapleaf with the shortest path.
+ */
+function findTapLeafToFinalize(input, inputIndex, leafHashToFinalize) {
+ if (!input.tapScriptSig || !input.tapScriptSig.length)
+ throw new Error(
+ `Can not finalize taproot input #${inputIndex}. No tapleaf script signature provided.`,
+ );
+ const tapLeaf = (input.tapLeafScript || [])
+ .sort((a, b) => a.controlBlock.length - b.controlBlock.length)
+ .find(leaf =>
+ canFinalizeLeaf(leaf, input.tapScriptSig, leafHashToFinalize),
+ );
+ if (!tapLeaf)
+ throw new Error(
+ `Can not finalize taproot input #${inputIndex}. Signature for tapleaf script not found.`,
+ );
+ return tapLeaf;
+}
+/**
+ * Determines whether a TapLeafScript can be finalized.
+ *
+ * @param leaf - The TapLeafScript to check.
+ * @param tapScriptSig - The array of TapScriptSig objects.
+ * @param hash - The optional hash to compare with the leaf hash.
+ * @returns A boolean indicating whether the TapLeafScript can be finalized.
+ */
+function canFinalizeLeaf(leaf, tapScriptSig, hash) {
+ const leafHash = (0, bip341_js_1.tapleafHash)({
+ output: leaf.script,
+ version: leaf.leafVersion,
+ });
+ const whiteListedHash = !hash || tools.compare(leafHash, hash) === 0;
+ return (
+ whiteListedHash &&
+ tapScriptSig.find(tss => tools.compare(tss.leafHash, leafHash) === 0) !==
+ undefined
+ );
+}
+/**
+ * Checks if the given PsbtInput or PsbtOutput has non-taproot fields.
+ * Non-taproot fields include redeemScript, witnessScript, and bip32Derivation.
+ * @param io The PsbtInput or PsbtOutput to check.
+ * @returns A boolean indicating whether the given input or output has non-taproot fields.
+ */
+function hasNonTaprootFields(io) {
+ return (
+ io &&
+ !!(
+ io.redeemScript ||
+ io.witnessScript ||
+ (io.bip32Derivation && io.bip32Derivation.length)
+ )
+ );
+}
diff --git a/src/cjs/psbt/bip371.d.ts b/src/cjs/psbt/bip371.d.ts
new file mode 100644
index 000000000..52e16c854
--- /dev/null
+++ b/src/cjs/psbt/bip371.d.ts
@@ -0,0 +1,89 @@
+import { Taptree } from '../types.js';
+import { PsbtInput, PsbtOutput, TapLeaf } from 'bip174';
+/**
+ * Converts a public key to an X-only public key.
+ * @param pubKey The public key to convert.
+ * @returns The X-only public key.
+ */
+export declare const toXOnly: (pubKey: Uint8Array) => Uint8Array;
+/**
+ * Default tapscript finalizer. It searches for the `tapLeafHashToFinalize` if provided.
+ * Otherwise it will search for the tapleaf that has at least one signature and has the shortest path.
+ * @param inputIndex the position of the PSBT input.
+ * @param input the PSBT input.
+ * @param tapLeafHashToFinalize optional, if provided the finalizer will search for a tapleaf that has this hash
+ * and will try to build the finalScriptWitness.
+ * @returns the finalScriptWitness or throws an exception if no tapleaf found.
+ */
+export declare function tapScriptFinalizer(inputIndex: number, input: PsbtInput, tapLeafHashToFinalize?: Uint8Array): {
+ finalScriptWitness: Uint8Array | undefined;
+};
+/**
+ * Serializes a taproot signature.
+ * @param sig The signature to serialize.
+ * @param sighashType The sighash type. Optional.
+ * @returns The serialized taproot signature.
+ */
+export declare function serializeTaprootSignature(sig: Uint8Array, sighashType?: number): Uint8Array;
+/**
+ * Checks if a PSBT input is a taproot input.
+ * @param input The PSBT input to check.
+ * @returns True if the input is a taproot input, false otherwise.
+ */
+export declare function isTaprootInput(input: PsbtInput): boolean;
+/**
+ * Checks if a PSBT output is a taproot output.
+ * @param output The PSBT output to check.
+ * @param script The script to check. Optional.
+ * @returns True if the output is a taproot output, false otherwise.
+ */
+export declare function isTaprootOutput(output: PsbtOutput, script?: Uint8Array): boolean;
+/**
+ * Checks the taproot input fields for consistency.
+ * @param inputData The original input data.
+ * @param newInputData The new input data.
+ * @param action The action being performed.
+ * @throws Throws an error if the input fields are inconsistent.
+ */
+export declare function checkTaprootInputFields(inputData: PsbtInput, newInputData: PsbtInput, action: string): void;
+/**
+ * Checks the taproot output fields for consistency.
+ * @param outputData The original output data.
+ * @param newOutputData The new output data.
+ * @param action The action being performed.
+ * @throws Throws an error if the output fields are inconsistent.
+ */
+export declare function checkTaprootOutputFields(outputData: PsbtOutput, newOutputData: PsbtOutput, action: string): void;
+/**
+ * Tweak the internal public key for a specific input.
+ * @param inputIndex - The index of the input.
+ * @param input - The PsbtInput object representing the input.
+ * @returns The tweaked internal public key.
+ * @throws Error if the tap internal key cannot be tweaked.
+ */
+export declare function tweakInternalPubKey(inputIndex: number, input: PsbtInput): Uint8Array;
+/**
+ * Convert a binary tree to a BIP371 type list. Each element of the list is (according to BIP371):
+ * One or more tuples representing the depth, leaf version, and script for a leaf in the Taproot tree,
+ * allowing the entire tree to be reconstructed. The tuples must be in depth first search order so that
+ * the tree is correctly reconstructed.
+ * @param tree the binary tap tree
+ * @returns a list of BIP 371 tapleaves
+ */
+export declare function tapTreeToList(tree: Taptree): TapLeaf[];
+/**
+ * Convert a BIP371 TapLeaf list to a TapTree (binary).
+ * @param leaves a list of tapleaves where each element of the list is (according to BIP371):
+ * One or more tuples representing the depth, leaf version, and script for a leaf in the Taproot tree,
+ * allowing the entire tree to be reconstructed. The tuples must be in depth first search order so that
+ * the tree is correctly reconstructed.
+ * @returns the corresponding taptree, or throws an exception if the tree cannot be reconstructed
+ */
+export declare function tapTreeFromList(leaves?: TapLeaf[]): Taptree;
+/**
+ * Checks the taproot input for signatures.
+ * @param input The PSBT input to check.
+ * @param action The action being performed.
+ * @returns True if the input has taproot signatures, false otherwise.
+ */
+export declare function checkTaprootInputForSigs(input: PsbtInput, action: string): boolean;
diff --git a/src/cjs/psbt/psbtutils.cjs b/src/cjs/psbt/psbtutils.cjs
new file mode 100644
index 000000000..6918f0226
--- /dev/null
+++ b/src/cjs/psbt/psbtutils.cjs
@@ -0,0 +1,228 @@
+'use strict';
+var __createBinding =
+ (this && this.__createBinding) ||
+ (Object.create
+ ? function (o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ var desc = Object.getOwnPropertyDescriptor(m, k);
+ if (
+ !desc ||
+ ('get' in desc ? !m.__esModule : desc.writable || desc.configurable)
+ ) {
+ desc = {
+ enumerable: true,
+ get: function () {
+ return m[k];
+ },
+ };
+ }
+ Object.defineProperty(o, k2, desc);
+ }
+ : function (o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ o[k2] = m[k];
+ });
+var __setModuleDefault =
+ (this && this.__setModuleDefault) ||
+ (Object.create
+ ? function (o, v) {
+ Object.defineProperty(o, 'default', { enumerable: true, value: v });
+ }
+ : function (o, v) {
+ o['default'] = v;
+ });
+var __importStar =
+ (this && this.__importStar) ||
+ function (mod) {
+ if (mod && mod.__esModule) return mod;
+ var result = {};
+ if (mod != null)
+ for (var k in mod)
+ if (k !== 'default' && Object.prototype.hasOwnProperty.call(mod, k))
+ __createBinding(result, mod, k);
+ __setModuleDefault(result, mod);
+ return result;
+ };
+Object.defineProperty(exports, '__esModule', { value: true });
+exports.isP2TR =
+ exports.isP2SHScript =
+ exports.isP2WSHScript =
+ exports.isP2WPKH =
+ exports.isP2PKH =
+ exports.isP2PK =
+ exports.isP2MS =
+ void 0;
+exports.witnessStackToScriptWitness = witnessStackToScriptWitness;
+exports.pubkeyPositionInScript = pubkeyPositionInScript;
+exports.pubkeyInScript = pubkeyInScript;
+exports.checkInputForSig = checkInputForSig;
+exports.signatureBlocksAction = signatureBlocksAction;
+const varuint = __importStar(require('varuint-bitcoin'));
+const bscript = __importStar(require('../script.cjs'));
+const transaction_js_1 = require('../transaction.cjs');
+const crypto_js_1 = require('../crypto.cjs');
+const payments = __importStar(require('../payments/index.cjs'));
+const tools = __importStar(require('uint8array-tools'));
+/**
+ * Checks if a given payment factory can generate a payment script from a given script.
+ * @param payment The payment factory to check.
+ * @returns A function that takes a script and returns a boolean indicating whether the payment factory can generate a payment script from the script.
+ */
+function isPaymentFactory(payment) {
+ return script => {
+ try {
+ payment({ output: script });
+ return true;
+ } catch (err) {
+ return false;
+ }
+ };
+}
+exports.isP2MS = isPaymentFactory(payments.p2ms);
+exports.isP2PK = isPaymentFactory(payments.p2pk);
+exports.isP2PKH = isPaymentFactory(payments.p2pkh);
+exports.isP2WPKH = isPaymentFactory(payments.p2wpkh);
+exports.isP2WSHScript = isPaymentFactory(payments.p2wsh);
+exports.isP2SHScript = isPaymentFactory(payments.p2sh);
+exports.isP2TR = isPaymentFactory(payments.p2tr);
+/**
+ * Converts a witness stack to a script witness.
+ * @param witness The witness stack to convert.
+ * @returns The script witness as a Buffer.
+ */
+function witnessStackToScriptWitness(witness) {
+ let buffer = new Uint8Array(0);
+ function writeSlice(slice) {
+ buffer = tools.concat([buffer, slice]);
+ }
+ function writeVarInt(i) {
+ const currentLen = buffer.length;
+ const varintLen = varuint.encodingLength(i);
+ buffer = tools.concat([buffer, new Uint8Array(varintLen)]);
+ varuint.encode(i, buffer, currentLen);
+ }
+ function writeVarSlice(slice) {
+ writeVarInt(slice.length);
+ writeSlice(slice);
+ }
+ function writeVector(vector) {
+ writeVarInt(vector.length);
+ vector.forEach(writeVarSlice);
+ }
+ writeVector(witness);
+ return buffer;
+}
+/**
+ * Finds the position of a public key in a script.
+ * @param pubkey The public key to search for.
+ * @param script The script to search in.
+ * @returns The index of the public key in the script, or -1 if not found.
+ * @throws {Error} If there is an unknown script error.
+ */
+function pubkeyPositionInScript(pubkey, script) {
+ const pubkeyHash = (0, crypto_js_1.hash160)(pubkey);
+ const pubkeyXOnly = pubkey.slice(1, 33); // slice before calling?
+ const decompiled = bscript.decompile(script);
+ if (decompiled === null) throw new Error('Unknown script error');
+ return decompiled.findIndex(element => {
+ if (typeof element === 'number') return false;
+ return (
+ tools.compare(pubkey, element) === 0 ||
+ tools.compare(pubkeyHash, element) === 0 ||
+ tools.compare(pubkeyXOnly, element) === 0
+ );
+ });
+}
+/**
+ * Checks if a public key is present in a script.
+ * @param pubkey The public key to check.
+ * @param script The script to search in.
+ * @returns A boolean indicating whether the public key is present in the script.
+ */
+function pubkeyInScript(pubkey, script) {
+ return pubkeyPositionInScript(pubkey, script) !== -1;
+}
+/**
+ * Checks if an input contains a signature for a specific action.
+ * @param input - The input to check.
+ * @param action - The action to check for.
+ * @returns A boolean indicating whether the input contains a signature for the specified action.
+ */
+function checkInputForSig(input, action) {
+ const pSigs = extractPartialSigs(input);
+ return pSigs.some(pSig =>
+ signatureBlocksAction(pSig, bscript.signature.decode, action),
+ );
+}
+/**
+ * Determines if a given action is allowed for a signature block.
+ * @param signature - The signature block.
+ * @param signatureDecodeFn - The function used to decode the signature.
+ * @param action - The action to be checked.
+ * @returns True if the action is allowed, false otherwise.
+ */
+function signatureBlocksAction(signature, signatureDecodeFn, action) {
+ const { hashType } = signatureDecodeFn(signature);
+ const whitelist = [];
+ const isAnyoneCanPay =
+ hashType & transaction_js_1.Transaction.SIGHASH_ANYONECANPAY;
+ if (isAnyoneCanPay) whitelist.push('addInput');
+ const hashMod = hashType & 0x1f;
+ switch (hashMod) {
+ case transaction_js_1.Transaction.SIGHASH_ALL:
+ break;
+ case transaction_js_1.Transaction.SIGHASH_SINGLE:
+ case transaction_js_1.Transaction.SIGHASH_NONE:
+ whitelist.push('addOutput');
+ whitelist.push('setInputSequence');
+ break;
+ }
+ if (whitelist.indexOf(action) === -1) {
+ return true;
+ }
+ return false;
+}
+/**
+ * Extracts the signatures from a PsbtInput object.
+ * If the input has partial signatures, it returns an array of the signatures.
+ * If the input does not have partial signatures, it checks if it has a finalScriptSig or finalScriptWitness.
+ * If it does, it extracts the signatures from the final scripts and returns them.
+ * If none of the above conditions are met, it returns an empty array.
+ *
+ * @param input - The PsbtInput object from which to extract the signatures.
+ * @returns An array of signatures extracted from the PsbtInput object.
+ */
+function extractPartialSigs(input) {
+ let pSigs = [];
+ if ((input.partialSig || []).length === 0) {
+ if (!input.finalScriptSig && !input.finalScriptWitness) return [];
+ pSigs = getPsigsFromInputFinalScripts(input);
+ } else {
+ pSigs = input.partialSig;
+ }
+ return pSigs.map(p => p.signature);
+}
+/**
+ * Retrieves the partial signatures (Psigs) from the input's final scripts.
+ * Psigs are extracted from both the final scriptSig and final scriptWitness of the input.
+ * Only canonical script signatures are considered.
+ *
+ * @param input - The PsbtInput object representing the input.
+ * @returns An array of PartialSig objects containing the extracted Psigs.
+ */
+function getPsigsFromInputFinalScripts(input) {
+ const scriptItems = !input.finalScriptSig
+ ? []
+ : bscript.decompile(input.finalScriptSig) || [];
+ const witnessItems = !input.finalScriptWitness
+ ? []
+ : bscript.decompile(input.finalScriptWitness) || [];
+ return scriptItems
+ .concat(witnessItems)
+ .filter(item => {
+ return (
+ item instanceof Uint8Array && bscript.isCanonicalScriptSignature(item)
+ );
+ })
+ .map(sig => ({ signature: sig }));
+}
diff --git a/src/cjs/psbt/psbtutils.d.ts b/src/cjs/psbt/psbtutils.d.ts
new file mode 100644
index 000000000..64955e0f5
--- /dev/null
+++ b/src/cjs/psbt/psbtutils.d.ts
@@ -0,0 +1,49 @@
+import { PsbtInput } from 'bip174';
+export declare const isP2MS: (script: Uint8Array) => boolean;
+export declare const isP2PK: (script: Uint8Array) => boolean;
+export declare const isP2PKH: (script: Uint8Array) => boolean;
+export declare const isP2WPKH: (script: Uint8Array) => boolean;
+export declare const isP2WSHScript: (script: Uint8Array) => boolean;
+export declare const isP2SHScript: (script: Uint8Array) => boolean;
+export declare const isP2TR: (script: Uint8Array) => boolean;
+/**
+ * Converts a witness stack to a script witness.
+ * @param witness The witness stack to convert.
+ * @returns The script witness as a Buffer.
+ */
+export declare function witnessStackToScriptWitness(witness: Uint8Array[]): Uint8Array;
+/**
+ * Finds the position of a public key in a script.
+ * @param pubkey The public key to search for.
+ * @param script The script to search in.
+ * @returns The index of the public key in the script, or -1 if not found.
+ * @throws {Error} If there is an unknown script error.
+ */
+export declare function pubkeyPositionInScript(pubkey: Uint8Array, script: Uint8Array): number;
+/**
+ * Checks if a public key is present in a script.
+ * @param pubkey The public key to check.
+ * @param script The script to search in.
+ * @returns A boolean indicating whether the public key is present in the script.
+ */
+export declare function pubkeyInScript(pubkey: Uint8Array, script: Uint8Array): boolean;
+/**
+ * Checks if an input contains a signature for a specific action.
+ * @param input - The input to check.
+ * @param action - The action to check for.
+ * @returns A boolean indicating whether the input contains a signature for the specified action.
+ */
+export declare function checkInputForSig(input: PsbtInput, action: string): boolean;
+type SignatureDecodeFunc = (buffer: Uint8Array) => {
+ signature: Uint8Array;
+ hashType: number;
+};
+/**
+ * Determines if a given action is allowed for a signature block.
+ * @param signature - The signature block.
+ * @param signatureDecodeFn - The function used to decode the signature.
+ * @param action - The action to be checked.
+ * @returns True if the action is allowed, false otherwise.
+ */
+export declare function signatureBlocksAction(signature: Uint8Array, signatureDecodeFn: SignatureDecodeFunc, action: string): boolean;
+export {};
diff --git a/src/cjs/push_data.cjs b/src/cjs/push_data.cjs
new file mode 100644
index 000000000..dd2888887
--- /dev/null
+++ b/src/cjs/push_data.cjs
@@ -0,0 +1,133 @@
+'use strict';
+var __createBinding =
+ (this && this.__createBinding) ||
+ (Object.create
+ ? function (o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ var desc = Object.getOwnPropertyDescriptor(m, k);
+ if (
+ !desc ||
+ ('get' in desc ? !m.__esModule : desc.writable || desc.configurable)
+ ) {
+ desc = {
+ enumerable: true,
+ get: function () {
+ return m[k];
+ },
+ };
+ }
+ Object.defineProperty(o, k2, desc);
+ }
+ : function (o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ o[k2] = m[k];
+ });
+var __setModuleDefault =
+ (this && this.__setModuleDefault) ||
+ (Object.create
+ ? function (o, v) {
+ Object.defineProperty(o, 'default', { enumerable: true, value: v });
+ }
+ : function (o, v) {
+ o['default'] = v;
+ });
+var __importStar =
+ (this && this.__importStar) ||
+ function (mod) {
+ if (mod && mod.__esModule) return mod;
+ var result = {};
+ if (mod != null)
+ for (var k in mod)
+ if (k !== 'default' && Object.prototype.hasOwnProperty.call(mod, k))
+ __createBinding(result, mod, k);
+ __setModuleDefault(result, mod);
+ return result;
+ };
+Object.defineProperty(exports, '__esModule', { value: true });
+exports.encodingLength = encodingLength;
+exports.encode = encode;
+exports.decode = decode;
+const ops_js_1 = require('./ops.cjs');
+const tools = __importStar(require('uint8array-tools'));
+/**
+ * Calculates the encoding length of a number used for push data in Bitcoin transactions.
+ * @param i The number to calculate the encoding length for.
+ * @returns The encoding length of the number.
+ */
+function encodingLength(i) {
+ return i < ops_js_1.OPS.OP_PUSHDATA1
+ ? 1
+ : i <= 0xff
+ ? 2
+ : i <= 0xffff
+ ? 3
+ : 5;
+}
+/**
+ * Encodes a number into a buffer using a variable-length encoding scheme.
+ * The encoded buffer is written starting at the specified offset.
+ * Returns the size of the encoded buffer.
+ *
+ * @param buffer - The buffer to write the encoded data into.
+ * @param num - The number to encode.
+ * @param offset - The offset at which to start writing the encoded buffer.
+ * @returns The size of the encoded buffer.
+ */
+function encode(buffer, num, offset) {
+ const size = encodingLength(num);
+ // ~6 bit
+ if (size === 1) {
+ tools.writeUInt8(buffer, offset, num);
+ // 8 bit
+ } else if (size === 2) {
+ tools.writeUInt8(buffer, offset, ops_js_1.OPS.OP_PUSHDATA1);
+ tools.writeUInt8(buffer, offset + 1, num);
+ // 16 bit
+ } else if (size === 3) {
+ tools.writeUInt8(buffer, offset, ops_js_1.OPS.OP_PUSHDATA2);
+ tools.writeUInt16(buffer, offset + 1, num, 'LE');
+ // 32 bit
+ } else {
+ tools.writeUInt8(buffer, offset, ops_js_1.OPS.OP_PUSHDATA4);
+ tools.writeUInt32(buffer, offset + 1, num, 'LE');
+ }
+ return size;
+}
+/**
+ * Decodes a buffer and returns information about the opcode, number, and size.
+ * @param buffer - The buffer to decode.
+ * @param offset - The offset within the buffer to start decoding.
+ * @returns An object containing the opcode, number, and size, or null if decoding fails.
+ */
+function decode(buffer, offset) {
+ const opcode = tools.readUInt8(buffer, offset);
+ let num;
+ let size;
+ // ~6 bit
+ if (opcode < ops_js_1.OPS.OP_PUSHDATA1) {
+ num = opcode;
+ size = 1;
+ // 8 bit
+ } else if (opcode === ops_js_1.OPS.OP_PUSHDATA1) {
+ if (offset + 2 > buffer.length) return null;
+ num = tools.readUInt8(buffer, offset + 1);
+ size = 2;
+ // 16 bit
+ } else if (opcode === ops_js_1.OPS.OP_PUSHDATA2) {
+ if (offset + 3 > buffer.length) return null;
+ num = tools.readUInt16(buffer, offset + 1, 'LE');
+ size = 3;
+ // 32 bit
+ } else {
+ if (offset + 5 > buffer.length) return null;
+ if (opcode !== ops_js_1.OPS.OP_PUSHDATA4)
+ throw new Error('Unexpected opcode');
+ num = tools.readUInt32(buffer, offset + 1, 'LE');
+ size = 5;
+ }
+ return {
+ opcode,
+ number: num,
+ size,
+ };
+}
diff --git a/src/cjs/push_data.d.ts b/src/cjs/push_data.d.ts
new file mode 100644
index 000000000..2ce910991
--- /dev/null
+++ b/src/cjs/push_data.d.ts
@@ -0,0 +1,28 @@
+/**
+ * Calculates the encoding length of a number used for push data in Bitcoin transactions.
+ * @param i The number to calculate the encoding length for.
+ * @returns The encoding length of the number.
+ */
+export declare function encodingLength(i: number): number;
+/**
+ * Encodes a number into a buffer using a variable-length encoding scheme.
+ * The encoded buffer is written starting at the specified offset.
+ * Returns the size of the encoded buffer.
+ *
+ * @param buffer - The buffer to write the encoded data into.
+ * @param num - The number to encode.
+ * @param offset - The offset at which to start writing the encoded buffer.
+ * @returns The size of the encoded buffer.
+ */
+export declare function encode(buffer: Uint8Array, num: number, offset: number): number;
+/**
+ * Decodes a buffer and returns information about the opcode, number, and size.
+ * @param buffer - The buffer to decode.
+ * @param offset - The offset within the buffer to start decoding.
+ * @returns An object containing the opcode, number, and size, or null if decoding fails.
+ */
+export declare function decode(buffer: Uint8Array, offset: number): {
+ opcode: number;
+ number: number;
+ size: number;
+} | null;
diff --git a/src/cjs/script.cjs b/src/cjs/script.cjs
new file mode 100644
index 000000000..d3653658e
--- /dev/null
+++ b/src/cjs/script.cjs
@@ -0,0 +1,339 @@
+'use strict';
+/**
+ * Script tools module for working with Bitcoin scripts.
+ * Provides utilities such as decompiling, compiling, converting to/from ASM, stack manipulation,
+ * and script validation functions.
+ *
+ * @packageDocumentation
+ */
+var __createBinding =
+ (this && this.__createBinding) ||
+ (Object.create
+ ? function (o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ var desc = Object.getOwnPropertyDescriptor(m, k);
+ if (
+ !desc ||
+ ('get' in desc ? !m.__esModule : desc.writable || desc.configurable)
+ ) {
+ desc = {
+ enumerable: true,
+ get: function () {
+ return m[k];
+ },
+ };
+ }
+ Object.defineProperty(o, k2, desc);
+ }
+ : function (o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ o[k2] = m[k];
+ });
+var __setModuleDefault =
+ (this && this.__setModuleDefault) ||
+ (Object.create
+ ? function (o, v) {
+ Object.defineProperty(o, 'default', { enumerable: true, value: v });
+ }
+ : function (o, v) {
+ o['default'] = v;
+ });
+var __importStar =
+ (this && this.__importStar) ||
+ function (mod) {
+ if (mod && mod.__esModule) return mod;
+ var result = {};
+ if (mod != null)
+ for (var k in mod)
+ if (k !== 'default' && Object.prototype.hasOwnProperty.call(mod, k))
+ __createBinding(result, mod, k);
+ __setModuleDefault(result, mod);
+ return result;
+ };
+Object.defineProperty(exports, '__esModule', { value: true });
+exports.signature = exports.number = exports.OPS = void 0;
+exports.isPushOnly = isPushOnly;
+exports.countNonPushOnlyOPs = countNonPushOnlyOPs;
+exports.compile = compile;
+exports.decompile = decompile;
+exports.toASM = toASM;
+exports.fromASM = fromASM;
+exports.toStack = toStack;
+exports.isCanonicalPubKey = isCanonicalPubKey;
+exports.isDefinedHashType = isDefinedHashType;
+exports.isCanonicalScriptSignature = isCanonicalScriptSignature;
+const bip66 = __importStar(require('./bip66.cjs'));
+const ops_js_1 = require('./ops.cjs');
+Object.defineProperty(exports, 'OPS', {
+ enumerable: true,
+ get: function () {
+ return ops_js_1.OPS;
+ },
+});
+const pushdata = __importStar(require('./push_data.cjs'));
+const scriptNumber = __importStar(require('./script_number.cjs'));
+const scriptSignature = __importStar(require('./script_signature.cjs'));
+const types = __importStar(require('./types.cjs'));
+const tools = __importStar(require('uint8array-tools'));
+const v = __importStar(require('valibot'));
+/** Base opcode for OP_INT values. */
+const OP_INT_BASE = ops_js_1.OPS.OP_RESERVED; // OP_1 - 1
+/** Validation schema for a Bitcoin script stack. */
+const StackSchema = v.array(v.union([v.instance(Uint8Array), v.number()]));
+/**
+ * Determines if a value corresponds to an OP_INT opcode.
+ *
+ * @param value - The opcode to check.
+ * @returns True if the value is an OP_INT, false otherwise.
+ */
+function isOPInt(value) {
+ return (
+ v.is(v.number(), value) &&
+ (value === ops_js_1.OPS.OP_0 ||
+ (value >= ops_js_1.OPS.OP_1 && value <= ops_js_1.OPS.OP_16) ||
+ value === ops_js_1.OPS.OP_1NEGATE)
+ );
+}
+/**
+ * Checks if a script chunk is push-only (contains only data or OP_INT opcodes).
+ *
+ * @param value - The chunk to check.
+ * @returns True if the chunk is push-only, false otherwise.
+ */
+function isPushOnlyChunk(value) {
+ return v.is(types.BufferSchema, value) || isOPInt(value);
+}
+/**
+ * Determines if a stack consists of only push operations.
+ *
+ * @param value - The stack to check.
+ * @returns True if all elements in the stack are push-only, false otherwise.
+ */
+function isPushOnly(value) {
+ return v.is(v.pipe(v.any(), v.everyItem(isPushOnlyChunk)), value);
+}
+/**
+ * Counts the number of non-push-only opcodes in a stack.
+ *
+ * @param value - The stack to analyze.
+ * @returns The count of non-push-only opcodes.
+ */
+function countNonPushOnlyOPs(value) {
+ return value.length - value.filter(isPushOnlyChunk).length;
+}
+/**
+ * Converts a minimal script buffer to its corresponding opcode, if applicable.
+ *
+ * @param buffer - The buffer to check.
+ * @returns The corresponding opcode or undefined if not minimal.
+ */
+function asMinimalOP(buffer) {
+ if (buffer.length === 0) return ops_js_1.OPS.OP_0;
+ if (buffer.length !== 1) return;
+ if (buffer[0] >= 1 && buffer[0] <= 16) return OP_INT_BASE + buffer[0];
+ if (buffer[0] === 0x81) return ops_js_1.OPS.OP_1NEGATE;
+}
+/**
+ * Determines if a buffer or stack is a Uint8Array.
+ *
+ * @param buf - The buffer or stack to check.
+ * @returns True if the input is a Uint8Array, false otherwise.
+ */
+function chunksIsBuffer(buf) {
+ return buf instanceof Uint8Array;
+}
+/**
+ * Determines if a buffer or stack is a valid stack.
+ *
+ * @param buf - The buffer or stack to check.
+ * @returns True if the input is a stack, false otherwise.
+ */
+function chunksIsArray(buf) {
+ return v.is(StackSchema, buf);
+}
+/**
+ * Determines if a single chunk is a Uint8Array.
+ *
+ * @param buf - The chunk to check.
+ * @returns True if the chunk is a Uint8Array, false otherwise.
+ */
+function singleChunkIsBuffer(buf) {
+ return buf instanceof Uint8Array;
+}
+/**
+ * Compiles an array of script chunks into a Uint8Array.
+ *
+ * @param chunks - The chunks to compile.
+ * @returns The compiled script as a Uint8Array.
+ * @throws Error if compilation fails.
+ */
+function compile(chunks) {
+ if (chunksIsBuffer(chunks)) return chunks;
+ v.parse(StackSchema, chunks);
+ const bufferSize = chunks.reduce((accum, chunk) => {
+ if (singleChunkIsBuffer(chunk)) {
+ // adhere to BIP62.3, minimal push policy
+ if (chunk.length === 1 && asMinimalOP(chunk) !== undefined) {
+ return accum + 1;
+ }
+ return accum + pushdata.encodingLength(chunk.length) + chunk.length;
+ }
+ return accum + 1;
+ }, 0);
+ const buffer = new Uint8Array(bufferSize);
+ let offset = 0;
+ chunks.forEach(chunk => {
+ if (singleChunkIsBuffer(chunk)) {
+ // adhere to BIP62.3, minimal push policy
+ const opcode = asMinimalOP(chunk);
+ if (opcode !== undefined) {
+ tools.writeUInt8(buffer, offset, opcode);
+ offset += 1;
+ return;
+ }
+ offset += pushdata.encode(buffer, chunk.length, offset);
+ buffer.set(chunk, offset);
+ offset += chunk.length;
+ // opcode
+ } else {
+ tools.writeUInt8(buffer, offset, chunk);
+ offset += 1;
+ }
+ });
+ if (offset !== buffer.length) throw new Error('Could not decode chunks');
+ return buffer;
+}
+/**
+ * Decompiles a script buffer into an array of chunks.
+ *
+ * @param buffer - The script buffer to decompile.
+ * @returns The decompiled chunks or null if decompilation fails.
+ */
+function decompile(buffer) {
+ if (chunksIsArray(buffer)) return buffer;
+ v.parse(types.BufferSchema, buffer);
+ const chunks = [];
+ let i = 0;
+ while (i < buffer.length) {
+ const opcode = buffer[i];
+ if (opcode > ops_js_1.OPS.OP_0 && opcode <= ops_js_1.OPS.OP_PUSHDATA4) {
+ const d = pushdata.decode(buffer, i);
+ // did reading a pushDataInt fail?
+ if (d === null) return null;
+ i += d.size;
+ // attempt to read too much data?
+ if (i + d.number > buffer.length) return null;
+ const data = buffer.slice(i, i + d.number);
+ i += d.number;
+ // decompile minimally
+ const op = asMinimalOP(data);
+ if (op !== undefined) {
+ chunks.push(op);
+ } else {
+ chunks.push(data);
+ }
+ } else {
+ chunks.push(opcode);
+ i += 1;
+ }
+ }
+ return chunks;
+}
+/**
+ * Converts the given chunks into an ASM (Assembly) string representation.
+ * If the chunks parameter is a Buffer, it will be decompiled into a Stack before conversion.
+ * @param chunks - The chunks to convert into ASM.
+ * @returns The ASM string representation of the chunks.
+ */
+function toASM(chunks) {
+ if (chunksIsBuffer(chunks)) {
+ chunks = decompile(chunks);
+ }
+ if (!chunks) {
+ throw new Error('Could not convert invalid chunks to ASM');
+ }
+ return chunks
+ .map(chunk => {
+ if (singleChunkIsBuffer(chunk)) {
+ const op = asMinimalOP(chunk);
+ if (op === undefined) return tools.toHex(chunk);
+ chunk = op;
+ }
+ // opcode!
+ return ops_js_1.OPS[chunk];
+ })
+ .join(' ');
+}
+/**
+ * Converts an ASM string to a Buffer.
+ * @param asm The ASM string to convert.
+ * @returns The converted Buffer.
+ */
+function fromASM(asm) {
+ v.parse(v.string(), asm);
+ // Compile the ASM string into a Uint8Array
+ return compile(
+ asm.split(' ').map(chunk => {
+ // Check if the chunk is an opcode
+ if (isNaN(Number(chunk)) && chunk in ops_js_1.OPS) {
+ return ops_js_1.OPS[chunk];
+ }
+ // Validate if the chunk is a hexadecimal string
+ v.parse(types.HexSchema, chunk);
+ // Convert the chunk to Uint8Array data
+ return tools.fromHex(chunk);
+ }),
+ );
+}
+/**
+ * Converts the given chunks into a stack of buffers.
+ *
+ * @param chunks - The chunks to convert.
+ * @returns The stack of buffers.
+ */
+function toStack(chunks) {
+ chunks = decompile(chunks);
+ v.parse(v.custom(isPushOnly), chunks);
+ return chunks.map(op => {
+ if (singleChunkIsBuffer(op)) return op;
+ if (op === ops_js_1.OPS.OP_0) return new Uint8Array(0);
+ return scriptNumber.encode(op - OP_INT_BASE);
+ });
+}
+/**
+ * Checks if the provided buffer is a canonical public key.
+ *
+ * @param buffer - The buffer to check, expected to be a Uint8Array.
+ * @returns A boolean indicating whether the buffer is a canonical public key.
+ */
+function isCanonicalPubKey(buffer) {
+ return types.isPoint(buffer);
+}
+/**
+ * Checks if the provided hash type is defined.
+ *
+ * A hash type is considered defined if its modified value (after masking with ~0x80)
+ * is greater than 0x00 and less than 0x04.
+ *
+ * @param hashType - The hash type to check.
+ * @returns True if the hash type is defined, false otherwise.
+ */
+function isDefinedHashType(hashType) {
+ const hashTypeMod = hashType & ~0x80;
+ return hashTypeMod > 0x00 && hashTypeMod < 0x04;
+}
+/**
+ * Checks if the provided buffer is a canonical script signature.
+ *
+ * A canonical script signature is a valid DER-encoded signature followed by a valid hash type byte.
+ *
+ * @param buffer - The buffer to check.
+ * @returns `true` if the buffer is a canonical script signature, `false` otherwise.
+ */
+function isCanonicalScriptSignature(buffer) {
+ if (!(buffer instanceof Uint8Array)) return false;
+ if (!isDefinedHashType(buffer[buffer.length - 1])) return false;
+ return bip66.check(buffer.slice(0, -1));
+}
+exports.number = scriptNumber;
+exports.signature = scriptSignature;
diff --git a/src/cjs/script.d.ts b/src/cjs/script.d.ts
new file mode 100644
index 000000000..b7dd4a177
--- /dev/null
+++ b/src/cjs/script.d.ts
@@ -0,0 +1,89 @@
+/**
+ * Script tools module for working with Bitcoin scripts.
+ * Provides utilities such as decompiling, compiling, converting to/from ASM, stack manipulation,
+ * and script validation functions.
+ *
+ * @packageDocumentation
+ */
+import { OPS } from './ops.js';
+import { Stack } from './payments/index.js';
+import * as scriptNumber from './script_number.js';
+import * as scriptSignature from './script_signature.js';
+export { OPS };
+/**
+ * Determines if a stack consists of only push operations.
+ *
+ * @param value - The stack to check.
+ * @returns True if all elements in the stack are push-only, false otherwise.
+ */
+export declare function isPushOnly(value: Stack): boolean;
+/**
+ * Counts the number of non-push-only opcodes in a stack.
+ *
+ * @param value - The stack to analyze.
+ * @returns The count of non-push-only opcodes.
+ */
+export declare function countNonPushOnlyOPs(value: Stack): number;
+/**
+ * Compiles an array of script chunks into a Uint8Array.
+ *
+ * @param chunks - The chunks to compile.
+ * @returns The compiled script as a Uint8Array.
+ * @throws Error if compilation fails.
+ */
+export declare function compile(chunks: Uint8Array | Stack): Uint8Array;
+/**
+ * Decompiles a script buffer into an array of chunks.
+ *
+ * @param buffer - The script buffer to decompile.
+ * @returns The decompiled chunks or null if decompilation fails.
+ */
+export declare function decompile(buffer: Uint8Array | Array): Array | null;
+/**
+ * Converts the given chunks into an ASM (Assembly) string representation.
+ * If the chunks parameter is a Buffer, it will be decompiled into a Stack before conversion.
+ * @param chunks - The chunks to convert into ASM.
+ * @returns The ASM string representation of the chunks.
+ */
+export declare function toASM(chunks: Uint8Array | Array): string;
+/**
+ * Converts an ASM string to a Buffer.
+ * @param asm The ASM string to convert.
+ * @returns The converted Buffer.
+ */
+export declare function fromASM(asm: string): Uint8Array;
+/**
+ * Converts the given chunks into a stack of buffers.
+ *
+ * @param chunks - The chunks to convert.
+ * @returns The stack of buffers.
+ */
+export declare function toStack(chunks: Uint8Array | Array): Uint8Array[];
+/**
+ * Checks if the provided buffer is a canonical public key.
+ *
+ * @param buffer - The buffer to check, expected to be a Uint8Array.
+ * @returns A boolean indicating whether the buffer is a canonical public key.
+ */
+export declare function isCanonicalPubKey(buffer: Uint8Array): boolean;
+/**
+ * Checks if the provided hash type is defined.
+ *
+ * A hash type is considered defined if its modified value (after masking with ~0x80)
+ * is greater than 0x00 and less than 0x04.
+ *
+ * @param hashType - The hash type to check.
+ * @returns True if the hash type is defined, false otherwise.
+ */
+export declare function isDefinedHashType(hashType: number): boolean;
+/**
+ * Checks if the provided buffer is a canonical script signature.
+ *
+ * A canonical script signature is a valid DER-encoded signature followed by a valid hash type byte.
+ *
+ * @param buffer - The buffer to check.
+ * @returns `true` if the buffer is a canonical script signature, `false` otherwise.
+ */
+export declare function isCanonicalScriptSignature(buffer: Uint8Array): boolean;
+export declare const number: typeof scriptNumber;
+export declare const signature: typeof scriptSignature;
diff --git a/src/cjs/script_number.cjs b/src/cjs/script_number.cjs
new file mode 100644
index 000000000..1ef0334f5
--- /dev/null
+++ b/src/cjs/script_number.cjs
@@ -0,0 +1,122 @@
+'use strict';
+var __createBinding =
+ (this && this.__createBinding) ||
+ (Object.create
+ ? function (o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ var desc = Object.getOwnPropertyDescriptor(m, k);
+ if (
+ !desc ||
+ ('get' in desc ? !m.__esModule : desc.writable || desc.configurable)
+ ) {
+ desc = {
+ enumerable: true,
+ get: function () {
+ return m[k];
+ },
+ };
+ }
+ Object.defineProperty(o, k2, desc);
+ }
+ : function (o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ o[k2] = m[k];
+ });
+var __setModuleDefault =
+ (this && this.__setModuleDefault) ||
+ (Object.create
+ ? function (o, v) {
+ Object.defineProperty(o, 'default', { enumerable: true, value: v });
+ }
+ : function (o, v) {
+ o['default'] = v;
+ });
+var __importStar =
+ (this && this.__importStar) ||
+ function (mod) {
+ if (mod && mod.__esModule) return mod;
+ var result = {};
+ if (mod != null)
+ for (var k in mod)
+ if (k !== 'default' && Object.prototype.hasOwnProperty.call(mod, k))
+ __createBinding(result, mod, k);
+ __setModuleDefault(result, mod);
+ return result;
+ };
+Object.defineProperty(exports, '__esModule', { value: true });
+exports.decode = decode;
+exports.encode = encode;
+const tools = __importStar(require('uint8array-tools'));
+/**
+ * Decodes a script number from a buffer.
+ *
+ * @param buffer - The buffer containing the script number.
+ * @param maxLength - The maximum length of the script number. Defaults to 4.
+ * @param minimal - Whether the script number should be minimal. Defaults to true.
+ * @returns The decoded script number.
+ * @throws {TypeError} If the script number overflows the maximum length.
+ * @throws {Error} If the script number is not minimally encoded when minimal is true.
+ */
+function decode(buffer, maxLength, minimal) {
+ maxLength = maxLength || 4;
+ minimal = minimal === undefined ? true : minimal;
+ const length = buffer.length;
+ if (length === 0) return 0;
+ if (length > maxLength) throw new TypeError('Script number overflow');
+ if (minimal) {
+ if ((buffer[length - 1] & 0x7f) === 0) {
+ if (length <= 1 || (buffer[length - 2] & 0x80) === 0)
+ throw new Error('Non-minimally encoded script number');
+ }
+ }
+ // 40-bit
+ if (length === 5) {
+ const a = tools.readUInt32(buffer, 0, 'LE');
+ const b = tools.readUInt8(buffer, 4);
+ if (b & 0x80) return -((b & ~0x80) * 0x100000000 + a);
+ return b * 0x100000000 + a;
+ }
+ // 32-bit / 24-bit / 16-bit / 8-bit
+ let result = 0;
+ for (let i = 0; i < length; ++i) {
+ result |= buffer[i] << (8 * i);
+ }
+ if (buffer[length - 1] & 0x80)
+ return -(result & ~(0x80 << (8 * (length - 1))));
+ return result;
+}
+function scriptNumSize(i) {
+ return i > 0x7fffffff
+ ? 5
+ : i > 0x7fffff
+ ? 4
+ : i > 0x7fff
+ ? 3
+ : i > 0x7f
+ ? 2
+ : i > 0x00
+ ? 1
+ : 0;
+}
+/**
+ * Encodes a number into a Uint8Array using a specific format.
+ *
+ * @param _number - The number to encode.
+ * @returns The encoded number as a Uint8Array.
+ */
+function encode(_number) {
+ let value = Math.abs(_number);
+ const size = scriptNumSize(value);
+ const buffer = new Uint8Array(size);
+ const negative = _number < 0;
+ for (let i = 0; i < size; ++i) {
+ tools.writeUInt8(buffer, i, value & 0xff);
+ value >>= 8;
+ }
+ if (buffer[size - 1] & 0x80) {
+ tools.writeUInt8(buffer, size - 1, negative ? 0x80 : 0x00);
+ } else if (negative) {
+ buffer[size - 1] |= 0x80;
+ }
+ return buffer;
+}
diff --git a/src/cjs/script_number.d.ts b/src/cjs/script_number.d.ts
new file mode 100644
index 000000000..26a2ea089
--- /dev/null
+++ b/src/cjs/script_number.d.ts
@@ -0,0 +1,18 @@
+/**
+ * Decodes a script number from a buffer.
+ *
+ * @param buffer - The buffer containing the script number.
+ * @param maxLength - The maximum length of the script number. Defaults to 4.
+ * @param minimal - Whether the script number should be minimal. Defaults to true.
+ * @returns The decoded script number.
+ * @throws {TypeError} If the script number overflows the maximum length.
+ * @throws {Error} If the script number is not minimally encoded when minimal is true.
+ */
+export declare function decode(buffer: Uint8Array, maxLength?: number, minimal?: boolean): number;
+/**
+ * Encodes a number into a Uint8Array using a specific format.
+ *
+ * @param _number - The number to encode.
+ * @returns The encoded number as a Uint8Array.
+ */
+export declare function encode(_number: number): Uint8Array;
diff --git a/src/cjs/script_signature.cjs b/src/cjs/script_signature.cjs
new file mode 100644
index 000000000..0ef4ee806
--- /dev/null
+++ b/src/cjs/script_signature.cjs
@@ -0,0 +1,123 @@
+'use strict';
+var __createBinding =
+ (this && this.__createBinding) ||
+ (Object.create
+ ? function (o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ var desc = Object.getOwnPropertyDescriptor(m, k);
+ if (
+ !desc ||
+ ('get' in desc ? !m.__esModule : desc.writable || desc.configurable)
+ ) {
+ desc = {
+ enumerable: true,
+ get: function () {
+ return m[k];
+ },
+ };
+ }
+ Object.defineProperty(o, k2, desc);
+ }
+ : function (o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ o[k2] = m[k];
+ });
+var __setModuleDefault =
+ (this && this.__setModuleDefault) ||
+ (Object.create
+ ? function (o, v) {
+ Object.defineProperty(o, 'default', { enumerable: true, value: v });
+ }
+ : function (o, v) {
+ o['default'] = v;
+ });
+var __importStar =
+ (this && this.__importStar) ||
+ function (mod) {
+ if (mod && mod.__esModule) return mod;
+ var result = {};
+ if (mod != null)
+ for (var k in mod)
+ if (k !== 'default' && Object.prototype.hasOwnProperty.call(mod, k))
+ __createBinding(result, mod, k);
+ __setModuleDefault(result, mod);
+ return result;
+ };
+Object.defineProperty(exports, '__esModule', { value: true });
+exports.decode = decode;
+exports.encode = encode;
+const bip66 = __importStar(require('./bip66.cjs'));
+const script_js_1 = require('./script.cjs');
+const v = __importStar(require('valibot'));
+const tools = __importStar(require('uint8array-tools'));
+const types_js_1 = require('./types.cjs');
+const ZERO = new Uint8Array(1);
+/**
+ * Converts a buffer to a DER-encoded buffer.
+ * @param x - The buffer to be converted.
+ * @returns The DER-encoded buffer.
+ */
+function toDER(x) {
+ let i = 0;
+ while (x[i] === 0) ++i;
+ if (i === x.length) return ZERO;
+ x = x.slice(i);
+ if (x[0] & 0x80) return tools.concat([ZERO, x]);
+ return x;
+}
+/**
+ * Converts a DER-encoded signature to a buffer.
+ * If the first byte of the input buffer is 0x00, it is skipped.
+ * The resulting buffer is 32 bytes long, filled with zeros if necessary.
+ * @param x - The DER-encoded signature.
+ * @returns The converted buffer.
+ */
+function fromDER(x) {
+ if (x[0] === 0x00) x = x.slice(1);
+ const buffer = new Uint8Array(32);
+ const bstart = Math.max(0, 32 - x.length);
+ buffer.set(x, bstart);
+ return buffer;
+}
+// BIP62: 1 byte hashType flag (only 0x01, 0x02, 0x03, 0x81, 0x82 and 0x83 are allowed)
+/**
+ * Decodes a buffer into a ScriptSignature object.
+ * @param buffer - The buffer to decode.
+ * @returns The decoded ScriptSignature object.
+ * @throws Error if the hashType is invalid.
+ */
+function decode(buffer) {
+ const hashType = tools.readUInt8(buffer, buffer.length - 1);
+ if (!(0, script_js_1.isDefinedHashType)(hashType)) {
+ throw new Error('Invalid hashType ' + hashType);
+ }
+ const decoded = bip66.decode(buffer.subarray(0, -1));
+ const r = fromDER(decoded.r);
+ const s = fromDER(decoded.s);
+ const signature = tools.concat([r, s]);
+ return { signature, hashType };
+}
+/**
+ * Encodes a signature and hash type into a buffer.
+ * @param signature - The signature to encode.
+ * @param hashType - The hash type to encode.
+ * @returns The encoded buffer.
+ * @throws Error if the hashType is invalid.
+ */
+function encode(signature, hashType) {
+ v.parse(
+ v.object({
+ signature: (0, types_js_1.NBufferSchemaFactory)(64),
+ hashType: types_js_1.UInt8Schema,
+ }),
+ { signature, hashType },
+ );
+ if (!(0, script_js_1.isDefinedHashType)(hashType)) {
+ throw new Error('Invalid hashType ' + hashType);
+ }
+ const hashTypeBuffer = new Uint8Array(1);
+ tools.writeUInt8(hashTypeBuffer, 0, hashType);
+ const r = toDER(signature.slice(0, 32));
+ const s = toDER(signature.slice(32, 64));
+ return tools.concat([bip66.encode(r, s), hashTypeBuffer]);
+}
diff --git a/src/cjs/script_signature.d.ts b/src/cjs/script_signature.d.ts
new file mode 100644
index 000000000..16f645869
--- /dev/null
+++ b/src/cjs/script_signature.d.ts
@@ -0,0 +1,20 @@
+interface ScriptSignature {
+ signature: Uint8Array;
+ hashType: number;
+}
+/**
+ * Decodes a buffer into a ScriptSignature object.
+ * @param buffer - The buffer to decode.
+ * @returns The decoded ScriptSignature object.
+ * @throws Error if the hashType is invalid.
+ */
+export declare function decode(buffer: Uint8Array): ScriptSignature;
+/**
+ * Encodes a signature and hash type into a buffer.
+ * @param signature - The signature to encode.
+ * @param hashType - The hash type to encode.
+ * @returns The encoded buffer.
+ * @throws Error if the hashType is invalid.
+ */
+export declare function encode(signature: Uint8Array, hashType: number): Uint8Array;
+export {};
diff --git a/src/cjs/transaction.cjs b/src/cjs/transaction.cjs
new file mode 100644
index 000000000..ac022e544
--- /dev/null
+++ b/src/cjs/transaction.cjs
@@ -0,0 +1,657 @@
+'use strict';
+var __createBinding =
+ (this && this.__createBinding) ||
+ (Object.create
+ ? function (o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ var desc = Object.getOwnPropertyDescriptor(m, k);
+ if (
+ !desc ||
+ ('get' in desc ? !m.__esModule : desc.writable || desc.configurable)
+ ) {
+ desc = {
+ enumerable: true,
+ get: function () {
+ return m[k];
+ },
+ };
+ }
+ Object.defineProperty(o, k2, desc);
+ }
+ : function (o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ o[k2] = m[k];
+ });
+var __setModuleDefault =
+ (this && this.__setModuleDefault) ||
+ (Object.create
+ ? function (o, v) {
+ Object.defineProperty(o, 'default', { enumerable: true, value: v });
+ }
+ : function (o, v) {
+ o['default'] = v;
+ });
+var __importStar =
+ (this && this.__importStar) ||
+ function (mod) {
+ if (mod && mod.__esModule) return mod;
+ var result = {};
+ if (mod != null)
+ for (var k in mod)
+ if (k !== 'default' && Object.prototype.hasOwnProperty.call(mod, k))
+ __createBinding(result, mod, k);
+ __setModuleDefault(result, mod);
+ return result;
+ };
+Object.defineProperty(exports, '__esModule', { value: true });
+exports.Transaction = void 0;
+const bufferutils_js_1 = require('./bufferutils.cjs');
+const bcrypto = __importStar(require('./crypto.cjs'));
+const sha256_1 = require('@noble/hashes/sha256');
+const bscript = __importStar(require('./script.cjs'));
+const script_js_1 = require('./script.cjs');
+const types = __importStar(require('./types.cjs'));
+const tools = __importStar(require('uint8array-tools'));
+const v = __importStar(require('valibot'));
+function varSliceSize(someScript) {
+ const length = someScript.length;
+ return bufferutils_js_1.varuint.encodingLength(length) + length;
+}
+function vectorSize(someVector) {
+ const length = someVector.length;
+ return (
+ bufferutils_js_1.varuint.encodingLength(length) +
+ someVector.reduce((sum, witness) => {
+ return sum + varSliceSize(witness);
+ }, 0)
+ );
+}
+const EMPTY_BUFFER = new Uint8Array(0);
+const EMPTY_WITNESS = [];
+const ZERO = tools.fromHex(
+ '0000000000000000000000000000000000000000000000000000000000000000',
+);
+const ONE = tools.fromHex(
+ '0000000000000000000000000000000000000000000000000000000000000001',
+);
+const VALUE_UINT64_MAX = tools.fromHex('ffffffffffffffff');
+const BLANK_OUTPUT = {
+ script: EMPTY_BUFFER,
+ valueBuffer: VALUE_UINT64_MAX,
+};
+function isOutput(out) {
+ return out.value !== undefined;
+}
+/**
+ * Represents a Bitcoin transaction.
+ */
+class Transaction {
+ static DEFAULT_SEQUENCE = 0xffffffff;
+ static SIGHASH_DEFAULT = 0x00;
+ static SIGHASH_ALL = 0x01;
+ static SIGHASH_NONE = 0x02;
+ static SIGHASH_SINGLE = 0x03;
+ static SIGHASH_ANYONECANPAY = 0x80;
+ static SIGHASH_OUTPUT_MASK = 0x03;
+ static SIGHASH_INPUT_MASK = 0x80;
+ static ADVANCED_TRANSACTION_MARKER = 0x00;
+ static ADVANCED_TRANSACTION_FLAG = 0x01;
+ static MWEB_PEGOUT_FLAG = 0x08;
+ static fromBuffer(buffer, _NO_STRICT) {
+ const bufferReader = new bufferutils_js_1.BufferReader(buffer);
+ const tx = new Transaction();
+ tx.version = bufferReader.readUInt32();
+ const marker = bufferReader.readUInt8();
+ const flag = bufferReader.readUInt8();
+ let hasWitnesses = false;
+ let hasMweb = false;
+ if (
+ marker === Transaction.ADVANCED_TRANSACTION_MARKER &&
+ flag === Transaction.ADVANCED_TRANSACTION_FLAG
+ ) {
+ hasWitnesses = true;
+ } else if (
+ marker === Transaction.ADVANCED_TRANSACTION_MARKER &&
+ flag === Transaction.MWEB_PEGOUT_FLAG
+ ) {
+ hasMweb = true;
+ } else {
+ bufferReader.offset -= 2;
+ }
+ const vinLen = bufferReader.readVarInt();
+ for (let i = 0; i < vinLen; ++i) {
+ tx.ins.push({
+ hash: bufferReader.readSlice(32),
+ index: bufferReader.readUInt32(),
+ script: bufferReader.readVarSlice(),
+ sequence: bufferReader.readUInt32(),
+ witness: EMPTY_WITNESS,
+ });
+ }
+ const voutLen = bufferReader.readVarInt();
+ for (let i = 0; i < voutLen; ++i) {
+ tx.outs.push({
+ value: bufferReader.readInt64(),
+ script: bufferReader.readVarSlice(),
+ });
+ }
+ if (hasWitnesses) {
+ for (let i = 0; i < vinLen; ++i) {
+ tx.ins[i].witness = bufferReader.readVector();
+ }
+ // was this pointless?
+ if (!tx.hasWitnesses())
+ throw new Error('Transaction has superfluous witness data');
+ }
+ // MWEB peg-out transactions have an extra 0x00 byte before locktime
+ if (hasMweb) {
+ bufferReader.readUInt8();
+ }
+ tx.locktime = bufferReader.readUInt32();
+ if (_NO_STRICT) return tx;
+ if (bufferReader.offset !== buffer.length)
+ throw new Error('Transaction has unexpected data');
+ return tx;
+ }
+ static fromHex(hex) {
+ return Transaction.fromBuffer(tools.fromHex(hex), false);
+ }
+ static isCoinbaseHash(buffer) {
+ v.parse(types.Hash256bitSchema, buffer);
+ for (let i = 0; i < 32; ++i) {
+ if (buffer[i] !== 0) return false;
+ }
+ return true;
+ }
+ version = 1;
+ locktime = 0;
+ ins = [];
+ outs = [];
+ isCoinbase() {
+ return (
+ this.ins.length === 1 && Transaction.isCoinbaseHash(this.ins[0].hash)
+ );
+ }
+ addInput(hash, index, sequence, scriptSig) {
+ v.parse(
+ v.tuple([
+ types.Hash256bitSchema,
+ types.UInt32Schema,
+ v.nullable(v.optional(types.UInt32Schema)),
+ v.nullable(v.optional(types.BufferSchema)),
+ ]),
+ [hash, index, sequence, scriptSig],
+ );
+ if (sequence === undefined || sequence === null) {
+ sequence = Transaction.DEFAULT_SEQUENCE;
+ }
+ // Add the input and return the input's index
+ return (
+ this.ins.push({
+ hash,
+ index,
+ script: scriptSig || EMPTY_BUFFER,
+ sequence: sequence,
+ witness: EMPTY_WITNESS,
+ }) - 1
+ );
+ }
+ addOutput(scriptPubKey, value) {
+ v.parse(v.tuple([types.BufferSchema, types.SatoshiSchema]), [
+ scriptPubKey,
+ value,
+ ]);
+ // Add the output and return the output's index
+ return (
+ this.outs.push({
+ script: scriptPubKey,
+ value,
+ }) - 1
+ );
+ }
+ hasWitnesses() {
+ return this.ins.some(x => {
+ return x.witness.length !== 0;
+ });
+ }
+ /**
+ * Check if this is a Litecoin MWEB peg-out transaction.
+ * MWEB peg-out transactions have:
+ * - At least one output starting with OP_8 (witness version 8)
+ * - At least one input with empty script (anyone-can-spend from HogEx)
+ */
+ isMwebPegOutTx() {
+ return (
+ this.outs.some(output => {
+ // Check if output script starts with OP_8 (0x58)
+ return (
+ output.script.length > 0 && output.script[0] === script_js_1.OPS.OP_8
+ );
+ }) && this.ins.some(input => input.script.length === 0)
+ );
+ }
+ stripWitnesses() {
+ this.ins.forEach(input => {
+ input.witness = EMPTY_WITNESS; // Set witness data to an empty array
+ });
+ }
+ weight() {
+ // Weight calculation excludes MWEB-specific bytes
+ const base = this.byteLength(false, false);
+ const total = this.byteLength(true, false);
+ return base * 3 + total;
+ }
+ virtualSize() {
+ return Math.ceil(this.weight() / 4);
+ }
+ byteLength(_ALLOW_WITNESS = true, _ALLOW_MWEB = true) {
+ const hasWitnesses = _ALLOW_WITNESS && this.hasWitnesses();
+ const hasMweb = _ALLOW_MWEB && !hasWitnesses && this.isMwebPegOutTx();
+ return (
+ (hasWitnesses ? 10 : 8) +
+ bufferutils_js_1.varuint.encodingLength(this.ins.length) +
+ bufferutils_js_1.varuint.encodingLength(this.outs.length) +
+ this.ins.reduce((sum, input) => {
+ return sum + 40 + varSliceSize(input.script);
+ }, 0) +
+ this.outs.reduce((sum, output) => {
+ return sum + 8 + varSliceSize(output.script);
+ }, 0) +
+ (hasWitnesses
+ ? this.ins.reduce((sum, input) => {
+ return sum + vectorSize(input.witness);
+ }, 0)
+ : 0) +
+ (hasMweb ? 3 : 0) // marker (1) + flag (1) + extra byte before locktime (1)
+ );
+ }
+ clone() {
+ const newTx = new Transaction();
+ newTx.version = this.version;
+ newTx.locktime = this.locktime;
+ newTx.ins = this.ins.map(txIn => {
+ return {
+ hash: txIn.hash,
+ index: txIn.index,
+ script: txIn.script,
+ sequence: txIn.sequence,
+ witness: txIn.witness,
+ };
+ });
+ newTx.outs = this.outs.map(txOut => {
+ return {
+ script: txOut.script,
+ value: txOut.value,
+ };
+ });
+ return newTx;
+ }
+ /**
+ * Hash transaction for signing a specific input.
+ *
+ * Bitcoin uses a different hash for each signed transaction input.
+ * This method copies the transaction, makes the necessary changes based on the
+ * hashType, and then hashes the result.
+ * This hash can then be used to sign the provided transaction input.
+ */
+ hashForSignature(inIndex, prevOutScript, hashType) {
+ v.parse(v.tuple([types.UInt32Schema, types.BufferSchema, v.number()]), [
+ inIndex,
+ prevOutScript,
+ hashType,
+ ]);
+ // https://github.com/bitcoin/bitcoin/blob/master/src/test/sighash_tests.cpp#L29
+ if (inIndex >= this.ins.length) return ONE;
+ // ignore OP_CODESEPARATOR
+ const ourScript = bscript.compile(
+ bscript.decompile(prevOutScript).filter(x => {
+ return x !== script_js_1.OPS.OP_CODESEPARATOR;
+ }),
+ );
+ const txTmp = this.clone();
+ // SIGHASH_NONE: ignore all outputs? (wildcard payee)
+ if ((hashType & 0x1f) === Transaction.SIGHASH_NONE) {
+ txTmp.outs = [];
+ // ignore sequence numbers (except at inIndex)
+ txTmp.ins.forEach((input, i) => {
+ if (i === inIndex) return;
+ input.sequence = 0;
+ });
+ // SIGHASH_SINGLE: ignore all outputs, except at the same index?
+ } else if ((hashType & 0x1f) === Transaction.SIGHASH_SINGLE) {
+ // https://github.com/bitcoin/bitcoin/blob/master/src/test/sighash_tests.cpp#L60
+ if (inIndex >= this.outs.length) return ONE;
+ // truncate outputs after
+ txTmp.outs.length = inIndex + 1;
+ // "blank" outputs before
+ for (let i = 0; i < inIndex; i++) {
+ txTmp.outs[i] = BLANK_OUTPUT;
+ }
+ // ignore sequence numbers (except at inIndex)
+ txTmp.ins.forEach((input, y) => {
+ if (y === inIndex) return;
+ input.sequence = 0;
+ });
+ }
+ // SIGHASH_ANYONECANPAY: ignore inputs entirely?
+ if (hashType & Transaction.SIGHASH_ANYONECANPAY) {
+ txTmp.ins = [txTmp.ins[inIndex]];
+ txTmp.ins[0].script = ourScript;
+ // SIGHASH_ALL: only ignore input scripts
+ } else {
+ // "blank" others input scripts
+ txTmp.ins.forEach(input => {
+ input.script = EMPTY_BUFFER;
+ });
+ txTmp.ins[inIndex].script = ourScript;
+ }
+ // serialize and hash
+ const buffer = new Uint8Array(txTmp.byteLength(false) + 4);
+ tools.writeInt32(buffer, buffer.length - 4, hashType, 'LE');
+ txTmp.__toBuffer(buffer, 0, false);
+ return bcrypto.hash256(buffer);
+ }
+ hashForWitnessV1(inIndex, prevOutScripts, values, hashType, leafHash, annex) {
+ // https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#common-signature-message
+ v.parse(
+ v.tuple([
+ types.UInt32Schema,
+ v.array(types.BufferSchema),
+ v.array(types.SatoshiSchema),
+ types.UInt32Schema,
+ ]),
+ [inIndex, prevOutScripts, values, hashType],
+ );
+ if (
+ values.length !== this.ins.length ||
+ prevOutScripts.length !== this.ins.length
+ ) {
+ throw new Error('Must supply prevout script and value for all inputs');
+ }
+ const outputType =
+ hashType === Transaction.SIGHASH_DEFAULT
+ ? Transaction.SIGHASH_ALL
+ : hashType & Transaction.SIGHASH_OUTPUT_MASK;
+ const inputType = hashType & Transaction.SIGHASH_INPUT_MASK;
+ const isAnyoneCanPay = inputType === Transaction.SIGHASH_ANYONECANPAY;
+ const isNone = outputType === Transaction.SIGHASH_NONE;
+ const isSingle = outputType === Transaction.SIGHASH_SINGLE;
+ let hashPrevouts = EMPTY_BUFFER;
+ let hashAmounts = EMPTY_BUFFER;
+ let hashScriptPubKeys = EMPTY_BUFFER;
+ let hashSequences = EMPTY_BUFFER;
+ let hashOutputs = EMPTY_BUFFER;
+ if (!isAnyoneCanPay) {
+ let bufferWriter = bufferutils_js_1.BufferWriter.withCapacity(
+ 36 * this.ins.length,
+ );
+ this.ins.forEach(txIn => {
+ bufferWriter.writeSlice(txIn.hash);
+ bufferWriter.writeUInt32(txIn.index);
+ });
+ hashPrevouts = (0, sha256_1.sha256)(bufferWriter.end());
+ bufferWriter = bufferutils_js_1.BufferWriter.withCapacity(
+ 8 * this.ins.length,
+ );
+ values.forEach(value => bufferWriter.writeInt64(value));
+ hashAmounts = (0, sha256_1.sha256)(bufferWriter.end());
+ bufferWriter = bufferutils_js_1.BufferWriter.withCapacity(
+ prevOutScripts.map(varSliceSize).reduce((a, b) => a + b),
+ );
+ prevOutScripts.forEach(prevOutScript =>
+ bufferWriter.writeVarSlice(prevOutScript),
+ );
+ hashScriptPubKeys = (0, sha256_1.sha256)(bufferWriter.end());
+ bufferWriter = bufferutils_js_1.BufferWriter.withCapacity(
+ 4 * this.ins.length,
+ );
+ this.ins.forEach(txIn => bufferWriter.writeUInt32(txIn.sequence));
+ hashSequences = (0, sha256_1.sha256)(bufferWriter.end());
+ }
+ if (!(isNone || isSingle)) {
+ if (!this.outs.length)
+ throw new Error('Add outputs to the transaction before signing.');
+ const txOutsSize = this.outs
+ .map(output => 8 + varSliceSize(output.script))
+ .reduce((a, b) => a + b);
+ const bufferWriter =
+ bufferutils_js_1.BufferWriter.withCapacity(txOutsSize);
+ this.outs.forEach(out => {
+ bufferWriter.writeInt64(out.value);
+ bufferWriter.writeVarSlice(out.script);
+ });
+ hashOutputs = (0, sha256_1.sha256)(bufferWriter.end());
+ } else if (isSingle && inIndex < this.outs.length) {
+ const output = this.outs[inIndex];
+ const bufferWriter = bufferutils_js_1.BufferWriter.withCapacity(
+ 8 + varSliceSize(output.script),
+ );
+ bufferWriter.writeInt64(output.value);
+ bufferWriter.writeVarSlice(output.script);
+ hashOutputs = (0, sha256_1.sha256)(bufferWriter.end());
+ }
+ const spendType = (leafHash ? 2 : 0) + (annex ? 1 : 0);
+ // Length calculation from:
+ // https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#cite_note-14
+ // With extension from:
+ // https://github.com/bitcoin/bips/blob/master/bip-0342.mediawiki#signature-validation
+ const sigMsgSize =
+ 174 -
+ (isAnyoneCanPay ? 49 : 0) -
+ (isNone ? 32 : 0) +
+ (annex ? 32 : 0) +
+ (leafHash ? 37 : 0);
+ const sigMsgWriter = bufferutils_js_1.BufferWriter.withCapacity(sigMsgSize);
+ sigMsgWriter.writeUInt8(hashType);
+ // Transaction
+ sigMsgWriter.writeUInt32(this.version);
+ sigMsgWriter.writeUInt32(this.locktime);
+ sigMsgWriter.writeSlice(hashPrevouts);
+ sigMsgWriter.writeSlice(hashAmounts);
+ sigMsgWriter.writeSlice(hashScriptPubKeys);
+ sigMsgWriter.writeSlice(hashSequences);
+ if (!(isNone || isSingle)) {
+ sigMsgWriter.writeSlice(hashOutputs);
+ }
+ // Input
+ sigMsgWriter.writeUInt8(spendType);
+ if (isAnyoneCanPay) {
+ const input = this.ins[inIndex];
+ sigMsgWriter.writeSlice(input.hash);
+ sigMsgWriter.writeUInt32(input.index);
+ sigMsgWriter.writeInt64(values[inIndex]);
+ sigMsgWriter.writeVarSlice(prevOutScripts[inIndex]);
+ sigMsgWriter.writeUInt32(input.sequence);
+ } else {
+ sigMsgWriter.writeUInt32(inIndex);
+ }
+ if (annex) {
+ const bufferWriter = bufferutils_js_1.BufferWriter.withCapacity(
+ varSliceSize(annex),
+ );
+ bufferWriter.writeVarSlice(annex);
+ sigMsgWriter.writeSlice((0, sha256_1.sha256)(bufferWriter.end()));
+ }
+ // Output
+ if (isSingle) {
+ sigMsgWriter.writeSlice(hashOutputs);
+ }
+ // BIP342 extension
+ if (leafHash) {
+ sigMsgWriter.writeSlice(leafHash);
+ sigMsgWriter.writeUInt8(0);
+ sigMsgWriter.writeUInt32(0xffffffff);
+ }
+ // Extra zero byte because:
+ // https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#cite_note-19
+ return bcrypto.taggedHash(
+ 'TapSighash',
+ tools.concat([Uint8Array.from([0x00]), sigMsgWriter.end()]),
+ );
+ }
+ hashForWitnessV0(inIndex, prevOutScript, value, hashType) {
+ v.parse(
+ v.tuple([
+ types.UInt32Schema,
+ types.BufferSchema,
+ types.SatoshiSchema,
+ types.UInt32Schema,
+ ]),
+ [inIndex, prevOutScript, value, hashType],
+ );
+ let tbuffer = Uint8Array.from([]);
+ let bufferWriter;
+ let hashOutputs = ZERO;
+ let hashPrevouts = ZERO;
+ let hashSequence = ZERO;
+ if (!(hashType & Transaction.SIGHASH_ANYONECANPAY)) {
+ tbuffer = new Uint8Array(36 * this.ins.length);
+ bufferWriter = new bufferutils_js_1.BufferWriter(tbuffer, 0);
+ this.ins.forEach(txIn => {
+ bufferWriter.writeSlice(txIn.hash);
+ bufferWriter.writeUInt32(txIn.index);
+ });
+ hashPrevouts = bcrypto.hash256(tbuffer);
+ }
+ if (
+ !(hashType & Transaction.SIGHASH_ANYONECANPAY) &&
+ (hashType & 0x1f) !== Transaction.SIGHASH_SINGLE &&
+ (hashType & 0x1f) !== Transaction.SIGHASH_NONE
+ ) {
+ tbuffer = new Uint8Array(4 * this.ins.length);
+ bufferWriter = new bufferutils_js_1.BufferWriter(tbuffer, 0);
+ this.ins.forEach(txIn => {
+ bufferWriter.writeUInt32(txIn.sequence);
+ });
+ hashSequence = bcrypto.hash256(tbuffer);
+ }
+ if (
+ (hashType & 0x1f) !== Transaction.SIGHASH_SINGLE &&
+ (hashType & 0x1f) !== Transaction.SIGHASH_NONE
+ ) {
+ const txOutsSize = this.outs.reduce((sum, output) => {
+ return sum + 8 + varSliceSize(output.script);
+ }, 0);
+ tbuffer = new Uint8Array(txOutsSize);
+ bufferWriter = new bufferutils_js_1.BufferWriter(tbuffer, 0);
+ this.outs.forEach(out => {
+ bufferWriter.writeInt64(out.value);
+ bufferWriter.writeVarSlice(out.script);
+ });
+ hashOutputs = bcrypto.hash256(tbuffer);
+ } else if (
+ (hashType & 0x1f) === Transaction.SIGHASH_SINGLE &&
+ inIndex < this.outs.length
+ ) {
+ const output = this.outs[inIndex];
+ tbuffer = new Uint8Array(8 + varSliceSize(output.script));
+ bufferWriter = new bufferutils_js_1.BufferWriter(tbuffer, 0);
+ bufferWriter.writeInt64(output.value);
+ bufferWriter.writeVarSlice(output.script);
+ hashOutputs = bcrypto.hash256(tbuffer);
+ }
+ tbuffer = new Uint8Array(156 + varSliceSize(prevOutScript));
+ bufferWriter = new bufferutils_js_1.BufferWriter(tbuffer, 0);
+ const input = this.ins[inIndex];
+ bufferWriter.writeUInt32(this.version);
+ bufferWriter.writeSlice(hashPrevouts);
+ bufferWriter.writeSlice(hashSequence);
+ bufferWriter.writeSlice(input.hash);
+ bufferWriter.writeUInt32(input.index);
+ bufferWriter.writeVarSlice(prevOutScript);
+ bufferWriter.writeInt64(value);
+ bufferWriter.writeUInt32(input.sequence);
+ bufferWriter.writeSlice(hashOutputs);
+ bufferWriter.writeUInt32(this.locktime);
+ bufferWriter.writeUInt32(hashType);
+ return bcrypto.hash256(tbuffer);
+ }
+ getHash(forWitness, forMweb) {
+ // wtxid for coinbase is always 32 bytes of 0x00
+ if (forWitness && this.isCoinbase()) return new Uint8Array(32);
+ // For MWEB transactions, txid is calculated WITHOUT the MWEB marker/flag/extra byte
+ // (similar to how SegWit txid excludes witness data)
+ const allowMweb = forMweb ?? false;
+ return bcrypto.hash256(
+ this.__toBuffer(undefined, undefined, forWitness, allowMweb),
+ );
+ }
+ getId() {
+ // transaction hash's are displayed in reverse order
+ // txid calculation excludes both witness and MWEB-specific data
+ return tools.toHex(
+ (0, bufferutils_js_1.reverseBuffer)(this.getHash(false, false)),
+ );
+ }
+ toBuffer(buffer, initialOffset) {
+ return this.__toBuffer(buffer, initialOffset, true);
+ }
+ toHex() {
+ return tools.toHex(this.toBuffer(undefined, undefined));
+ }
+ setInputScript(index, scriptSig) {
+ v.parse(v.tuple([v.number(), types.BufferSchema]), [index, scriptSig]);
+ this.ins[index].script = scriptSig;
+ }
+ setWitness(index, witness) {
+ v.parse(v.tuple([v.number(), v.array(types.BufferSchema)]), [
+ index,
+ witness,
+ ]);
+ this.ins[index].witness = witness;
+ }
+ __toBuffer(
+ buffer,
+ initialOffset,
+ _ALLOW_WITNESS = false,
+ _ALLOW_MWEB = true,
+ ) {
+ if (!buffer)
+ buffer = new Uint8Array(this.byteLength(_ALLOW_WITNESS, _ALLOW_MWEB));
+ const bufferWriter = new bufferutils_js_1.BufferWriter(
+ buffer,
+ initialOffset || 0,
+ );
+ bufferWriter.writeUInt32(this.version);
+ const hasWitnesses = _ALLOW_WITNESS && this.hasWitnesses();
+ const hasMweb = _ALLOW_MWEB && !hasWitnesses && this.isMwebPegOutTx();
+ if (hasWitnesses) {
+ bufferWriter.writeUInt8(Transaction.ADVANCED_TRANSACTION_MARKER);
+ bufferWriter.writeUInt8(Transaction.ADVANCED_TRANSACTION_FLAG);
+ } else if (hasMweb) {
+ bufferWriter.writeUInt8(Transaction.ADVANCED_TRANSACTION_MARKER);
+ bufferWriter.writeUInt8(Transaction.MWEB_PEGOUT_FLAG);
+ }
+ bufferWriter.writeVarInt(this.ins.length);
+ this.ins.forEach(txIn => {
+ bufferWriter.writeSlice(txIn.hash);
+ bufferWriter.writeUInt32(txIn.index);
+ bufferWriter.writeVarSlice(txIn.script);
+ bufferWriter.writeUInt32(txIn.sequence);
+ });
+ bufferWriter.writeVarInt(this.outs.length);
+ this.outs.forEach(txOut => {
+ if (isOutput(txOut)) {
+ bufferWriter.writeInt64(txOut.value);
+ } else {
+ bufferWriter.writeSlice(txOut.valueBuffer);
+ }
+ bufferWriter.writeVarSlice(txOut.script);
+ });
+ if (hasWitnesses) {
+ this.ins.forEach(input => {
+ bufferWriter.writeVector(input.witness);
+ });
+ }
+ // MWEB peg-out transactions have an extra 0x00 byte before locktime
+ if (hasMweb) {
+ bufferWriter.writeUInt8(0);
+ }
+ bufferWriter.writeUInt32(this.locktime);
+ // avoid slicing unless necessary
+ if (initialOffset !== undefined)
+ return buffer.slice(initialOffset, bufferWriter.offset);
+ return buffer;
+ }
+}
+exports.Transaction = Transaction;
diff --git a/src/cjs/transaction.d.ts b/src/cjs/transaction.d.ts
new file mode 100644
index 000000000..18c86a1d1
--- /dev/null
+++ b/src/cjs/transaction.d.ts
@@ -0,0 +1,68 @@
+export interface Output {
+ script: Uint8Array;
+ value: bigint;
+}
+export interface Input {
+ hash: Uint8Array;
+ index: number;
+ script: Uint8Array;
+ sequence: number;
+ witness: Uint8Array[];
+}
+/**
+ * Represents a Bitcoin transaction.
+ */
+export declare class Transaction {
+ static readonly DEFAULT_SEQUENCE = 4294967295;
+ static readonly SIGHASH_DEFAULT = 0;
+ static readonly SIGHASH_ALL = 1;
+ static readonly SIGHASH_NONE = 2;
+ static readonly SIGHASH_SINGLE = 3;
+ static readonly SIGHASH_ANYONECANPAY = 128;
+ static readonly SIGHASH_OUTPUT_MASK = 3;
+ static readonly SIGHASH_INPUT_MASK = 128;
+ static readonly ADVANCED_TRANSACTION_MARKER = 0;
+ static readonly ADVANCED_TRANSACTION_FLAG = 1;
+ static readonly MWEB_PEGOUT_FLAG = 8;
+ static fromBuffer(buffer: Uint8Array, _NO_STRICT?: boolean): Transaction;
+ static fromHex(hex: string): Transaction;
+ static isCoinbaseHash(buffer: Uint8Array): boolean;
+ version: number;
+ locktime: number;
+ ins: Input[];
+ outs: Output[];
+ isCoinbase(): boolean;
+ addInput(hash: Uint8Array, index: number, sequence?: number, scriptSig?: Uint8Array): number;
+ addOutput(scriptPubKey: Uint8Array, value: bigint): number;
+ hasWitnesses(): boolean;
+ /**
+ * Check if this is a Litecoin MWEB peg-out transaction.
+ * MWEB peg-out transactions have:
+ * - At least one output starting with OP_8 (witness version 8)
+ * - At least one input with empty script (anyone-can-spend from HogEx)
+ */
+ isMwebPegOutTx(): boolean;
+ stripWitnesses(): void;
+ weight(): number;
+ virtualSize(): number;
+ byteLength(_ALLOW_WITNESS?: boolean, _ALLOW_MWEB?: boolean): number;
+ clone(): Transaction;
+ /**
+ * Hash transaction for signing a specific input.
+ *
+ * Bitcoin uses a different hash for each signed transaction input.
+ * This method copies the transaction, makes the necessary changes based on the
+ * hashType, and then hashes the result.
+ * This hash can then be used to sign the provided transaction input.
+ */
+ hashForSignature(inIndex: number, prevOutScript: Uint8Array, hashType: number): Uint8Array;
+ hashForWitnessV1(inIndex: number, prevOutScripts: Uint8Array[], values: bigint[], hashType: number, leafHash?: Uint8Array, annex?: Uint8Array): Uint8Array;
+ hashForWitnessV0(inIndex: number, prevOutScript: Uint8Array, value: bigint, hashType: number): Uint8Array;
+ getHash(forWitness?: boolean, forMweb?: boolean): Uint8Array;
+ getId(): string;
+ toBuffer(buffer?: Uint8Array, initialOffset?: number): Uint8Array;
+ toHex(): string;
+ setInputScript(index: number, scriptSig: Uint8Array): void;
+ setWitness(index: number, witness: Uint8Array[]): void;
+ private __toBuffer;
+}
diff --git a/src/cjs/types.cjs b/src/cjs/types.cjs
new file mode 100644
index 000000000..0d5794818
--- /dev/null
+++ b/src/cjs/types.cjs
@@ -0,0 +1,147 @@
+'use strict';
+var __createBinding =
+ (this && this.__createBinding) ||
+ (Object.create
+ ? function (o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ var desc = Object.getOwnPropertyDescriptor(m, k);
+ if (
+ !desc ||
+ ('get' in desc ? !m.__esModule : desc.writable || desc.configurable)
+ ) {
+ desc = {
+ enumerable: true,
+ get: function () {
+ return m[k];
+ },
+ };
+ }
+ Object.defineProperty(o, k2, desc);
+ }
+ : function (o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ o[k2] = m[k];
+ });
+var __setModuleDefault =
+ (this && this.__setModuleDefault) ||
+ (Object.create
+ ? function (o, v) {
+ Object.defineProperty(o, 'default', { enumerable: true, value: v });
+ }
+ : function (o, v) {
+ o['default'] = v;
+ });
+var __importStar =
+ (this && this.__importStar) ||
+ function (mod) {
+ if (mod && mod.__esModule) return mod;
+ var result = {};
+ if (mod != null)
+ for (var k in mod)
+ if (k !== 'default' && Object.prototype.hasOwnProperty.call(mod, k))
+ __createBinding(result, mod, k);
+ __setModuleDefault(result, mod);
+ return result;
+ };
+Object.defineProperty(exports, '__esModule', { value: true });
+exports.NullablePartial =
+ exports.SatoshiSchema =
+ exports.UInt32Schema =
+ exports.UInt8Schema =
+ exports.HexSchema =
+ exports.BufferSchema =
+ exports.Hash256bitSchema =
+ exports.Hash160bitSchema =
+ exports.Buffer256bitSchema =
+ exports.TAPLEAF_VERSION_MASK =
+ exports.NBufferSchemaFactory =
+ void 0;
+exports.stacksEqual = stacksEqual;
+exports.isPoint = isPoint;
+exports.isTapleaf = isTapleaf;
+exports.isTaptree = isTaptree;
+const tools = __importStar(require('uint8array-tools'));
+const v = __importStar(require('valibot'));
+const ZERO32 = new Uint8Array(32);
+const EC_P = tools.fromHex(
+ 'fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f',
+);
+const NBufferSchemaFactory = size =>
+ v.pipe(v.instance(Uint8Array), v.length(size));
+exports.NBufferSchemaFactory = NBufferSchemaFactory;
+/**
+ * Checks if two arrays of Buffers are equal.
+ * @param a - The first array of Buffers.
+ * @param b - The second array of Buffers.
+ * @returns True if the arrays are equal, false otherwise.
+ */
+function stacksEqual(a, b) {
+ if (a.length !== b.length) return false;
+ return a.every((x, i) => {
+ return tools.compare(x, b[i]) === 0;
+ });
+}
+/**
+ * Checks if the given value is a valid elliptic curve point.
+ * @param p - The value to check.
+ * @returns True if the value is a valid elliptic curve point, false otherwise.
+ */
+function isPoint(p) {
+ if (!(p instanceof Uint8Array)) return false;
+ if (p.length < 33) return false;
+ const t = p[0];
+ const x = p.slice(1, 33);
+ if (tools.compare(ZERO32, x) === 0) return false;
+ if (tools.compare(x, EC_P) >= 0) return false;
+ if ((t === 0x02 || t === 0x03) && p.length === 33) {
+ return true;
+ }
+ const y = p.slice(33);
+ if (tools.compare(ZERO32, y) === 0) return false;
+ if (tools.compare(y, EC_P) >= 0) return false;
+ if (t === 0x04 && p.length === 65) return true;
+ return false;
+}
+exports.TAPLEAF_VERSION_MASK = 0xfe;
+function isTapleaf(o) {
+ if (!o || !('output' in o)) return false;
+ if (!(o.output instanceof Uint8Array)) return false;
+ if (o.version !== undefined)
+ return (o.version & exports.TAPLEAF_VERSION_MASK) === o.version;
+ return true;
+}
+function isTaptree(scriptTree) {
+ if (!Array.isArray(scriptTree)) return isTapleaf(scriptTree);
+ if (scriptTree.length !== 2) return false;
+ return scriptTree.every(t => isTaptree(t));
+}
+exports.Buffer256bitSchema = (0, exports.NBufferSchemaFactory)(32);
+exports.Hash160bitSchema = (0, exports.NBufferSchemaFactory)(20);
+exports.Hash256bitSchema = (0, exports.NBufferSchemaFactory)(32);
+exports.BufferSchema = v.instance(Uint8Array);
+exports.HexSchema = v.pipe(v.string(), v.regex(/^([0-9a-f]{2})+$/i));
+exports.UInt8Schema = v.pipe(
+ v.number(),
+ v.integer(),
+ v.minValue(0),
+ v.maxValue(0xff),
+);
+exports.UInt32Schema = v.pipe(
+ v.number(),
+ v.integer(),
+ v.minValue(0),
+ v.maxValue(0xffffffff),
+);
+exports.SatoshiSchema = v.pipe(
+ v.bigint(),
+ v.minValue(0n),
+ v.maxValue(0x7fffffffffffffffn),
+);
+const NullablePartial = a =>
+ v.object(
+ Object.entries(a).reduce(
+ (acc, next) => ({ ...acc, [next[0]]: v.nullish(next[1]) }),
+ {},
+ ),
+ );
+exports.NullablePartial = NullablePartial;
diff --git a/src/cjs/types.d.ts b/src/cjs/types.d.ts
new file mode 100644
index 000000000..7abcd67bb
--- /dev/null
+++ b/src/cjs/types.d.ts
@@ -0,0 +1,45 @@
+import * as v from 'valibot';
+export declare const NBufferSchemaFactory: (size: number) => v.SchemaWithPipe<[v.InstanceSchema, v.LengthAction]>;
+/**
+ * Checks if two arrays of Buffers are equal.
+ * @param a - The first array of Buffers.
+ * @param b - The second array of Buffers.
+ * @returns True if the arrays are equal, false otherwise.
+ */
+export declare function stacksEqual(a: Uint8Array[], b: Uint8Array[]): boolean;
+/**
+ * Checks if the given value is a valid elliptic curve point.
+ * @param p - The value to check.
+ * @returns True if the value is a valid elliptic curve point, false otherwise.
+ */
+export declare function isPoint(p: Uint8Array | number | undefined | null): boolean;
+export interface XOnlyPointAddTweakResult {
+ parity: 1 | 0;
+ xOnlyPubkey: Uint8Array;
+}
+export interface Tapleaf {
+ output: Uint8Array;
+ version?: number;
+}
+export declare const TAPLEAF_VERSION_MASK = 254;
+export declare function isTapleaf(o: any): o is Tapleaf;
+/**
+ * Binary tree repsenting script path spends for a Taproot input.
+ * Each node is either a single Tapleaf, or a pair of Tapleaf | Taptree.
+ * The tree has no balancing requirements.
+ */
+export type Taptree = [Taptree | Tapleaf, Taptree | Tapleaf] | Tapleaf;
+export declare function isTaptree(scriptTree: any): scriptTree is Taptree;
+export interface TinySecp256k1Interface {
+ isXOnlyPoint(p: Uint8Array): boolean;
+ xOnlyPointAddTweak(p: Uint8Array, tweak: Uint8Array): XOnlyPointAddTweakResult | null;
+}
+export declare const Buffer256bitSchema: v.SchemaWithPipe<[v.InstanceSchema, v.LengthAction]>;
+export declare const Hash160bitSchema: v.SchemaWithPipe<[v.InstanceSchema, v.LengthAction]>;
+export declare const Hash256bitSchema: v.SchemaWithPipe<[v.InstanceSchema, v.LengthAction]>;
+export declare const BufferSchema: v.InstanceSchema;
+export declare const HexSchema: v.SchemaWithPipe<[v.StringSchema, v.RegexAction]>;
+export declare const UInt8Schema: v.SchemaWithPipe<[v.NumberSchema, v.IntegerAction, v.MinValueAction, v.MaxValueAction]>;
+export declare const UInt32Schema: v.SchemaWithPipe<[v.NumberSchema, v.IntegerAction, v.MinValueAction, v.MaxValueAction]>;
+export declare const SatoshiSchema: v.SchemaWithPipe<[v.BigintSchema, v.MinValueAction, v.MaxValueAction]>;
+export declare const NullablePartial: (a: Record) => v.ObjectSchema<{}, undefined>;
diff --git a/src/crypto.d.ts b/src/crypto.d.ts
deleted file mode 100644
index ec088f3e3..000000000
--- a/src/crypto.d.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-///
-export declare function ripemd160(buffer: Buffer): Buffer;
-export declare function sha1(buffer: Buffer): Buffer;
-export declare function sha256(buffer: Buffer): Buffer;
-export declare function hash160(buffer: Buffer): Buffer;
-export declare function hash256(buffer: Buffer): Buffer;
-declare const TAGS: readonly ["BIP0340/challenge", "BIP0340/aux", "BIP0340/nonce", "TapLeaf", "TapBranch", "TapSighash", "TapTweak", "KeyAgg list", "KeyAgg coefficient"];
-export declare type TaggedHashPrefix = typeof TAGS[number];
-export declare function taggedHash(prefix: TaggedHashPrefix, data: Buffer): Buffer;
-export {};
diff --git a/src/crypto.js b/src/crypto.js
deleted file mode 100644
index 3c308da11..000000000
--- a/src/crypto.js
+++ /dev/null
@@ -1,58 +0,0 @@
-'use strict';
-Object.defineProperty(exports, '__esModule', { value: true });
-exports.taggedHash = exports.hash256 = exports.hash160 = exports.sha256 = exports.sha1 = exports.ripemd160 = void 0;
-const createHash = require('create-hash');
-function ripemd160(buffer) {
- try {
- return createHash('rmd160')
- .update(buffer)
- .digest();
- } catch (err) {
- return createHash('ripemd160')
- .update(buffer)
- .digest();
- }
-}
-exports.ripemd160 = ripemd160;
-function sha1(buffer) {
- return createHash('sha1')
- .update(buffer)
- .digest();
-}
-exports.sha1 = sha1;
-function sha256(buffer) {
- return createHash('sha256')
- .update(buffer)
- .digest();
-}
-exports.sha256 = sha256;
-function hash160(buffer) {
- return ripemd160(sha256(buffer));
-}
-exports.hash160 = hash160;
-function hash256(buffer) {
- return sha256(sha256(buffer));
-}
-exports.hash256 = hash256;
-const TAGS = [
- 'BIP0340/challenge',
- 'BIP0340/aux',
- 'BIP0340/nonce',
- 'TapLeaf',
- 'TapBranch',
- 'TapSighash',
- 'TapTweak',
- 'KeyAgg list',
- 'KeyAgg coefficient',
-];
-/** An object mapping tags to their tagged hash prefix of [SHA256(tag) | SHA256(tag)] */
-const TAGGED_HASH_PREFIXES = Object.fromEntries(
- TAGS.map(tag => {
- const tagHash = sha256(Buffer.from(tag));
- return [tag, Buffer.concat([tagHash, tagHash])];
- }),
-);
-function taggedHash(prefix, data) {
- return sha256(Buffer.concat([TAGGED_HASH_PREFIXES[prefix], data]));
-}
-exports.taggedHash = taggedHash;
diff --git a/src/esm/address.js b/src/esm/address.js
new file mode 100644
index 000000000..5908238fc
--- /dev/null
+++ b/src/esm/address.js
@@ -0,0 +1,200 @@
+import * as networks from './networks.js';
+import * as payments from './payments/index.js';
+import * as bscript from './script.js';
+import { Hash160bitSchema, UInt8Schema } from './types.js';
+import { bech32, bech32m } from 'bech32';
+import bs58check from 'bs58check';
+import * as tools from 'uint8array-tools';
+import * as v from 'valibot';
+const FUTURE_SEGWIT_MAX_SIZE = 40;
+const FUTURE_SEGWIT_MIN_SIZE = 2;
+const FUTURE_SEGWIT_MAX_VERSION = 16;
+const FUTURE_SEGWIT_MIN_VERSION = 2;
+const FUTURE_SEGWIT_VERSION_DIFF = 0x50;
+const FUTURE_SEGWIT_VERSION_WARNING =
+ 'WARNING: Sending to a future segwit version address can lead to loss of funds. ' +
+ 'End users MUST be warned carefully in the GUI and asked if they wish to proceed ' +
+ 'with caution. Wallets should verify the segwit version from the output of fromBech32, ' +
+ 'then decide when it is safe to use which version of segwit.';
+const WARNING_STATES = [false, false];
+/**
+ * Converts an output buffer to a future segwit address.
+ * @param output - The output buffer.
+ * @param network - The network object.
+ * @returns The future segwit address.
+ * @throws {TypeError} If the program length or version is invalid for segwit address.
+ */
+function _toFutureSegwitAddress(output, network) {
+ const data = output.slice(2);
+ if (
+ data.length < FUTURE_SEGWIT_MIN_SIZE ||
+ data.length > FUTURE_SEGWIT_MAX_SIZE
+ )
+ throw new TypeError('Invalid program length for segwit address');
+ const version = output[0] - FUTURE_SEGWIT_VERSION_DIFF;
+ if (
+ version < FUTURE_SEGWIT_MIN_VERSION ||
+ version > FUTURE_SEGWIT_MAX_VERSION
+ )
+ throw new TypeError('Invalid version for segwit address');
+ if (output[1] !== data.length)
+ throw new TypeError('Invalid script for segwit address');
+ if (WARNING_STATES[0] === false) {
+ console.warn(FUTURE_SEGWIT_VERSION_WARNING);
+ WARNING_STATES[0] = true;
+ }
+ return toBech32(data, version, network.bech32);
+}
+/**
+ * Decodes a base58check encoded Bitcoin address and returns the version and hash.
+ *
+ * @param address - The base58check encoded Bitcoin address to decode.
+ * @returns An object containing the version and hash of the decoded address.
+ * @throws {TypeError} If the address is too short or too long.
+ */
+export function fromBase58Check(address) {
+ const payload = bs58check.decode(address);
+ // TODO: 4.0.0, move to "toOutputScript"
+ if (payload.length < 21) throw new TypeError(address + ' is too short');
+ if (payload.length > 21) throw new TypeError(address + ' is too long');
+ const version = tools.readUInt8(payload, 0);
+ const hash = payload.slice(1);
+ return { version, hash };
+}
+/**
+ * Converts a Bech32 or Bech32m encoded address to its corresponding data representation.
+ * @param address - The Bech32 or Bech32m encoded address.
+ * @returns An object containing the version, prefix, and data of the address.
+ * @throws {TypeError} If the address uses the wrong encoding.
+ */
+export function fromBech32(address) {
+ let result;
+ let version;
+ try {
+ result = bech32.decode(address);
+ } catch (e) {}
+ if (result) {
+ version = result.words[0];
+ if (version !== 0) throw new TypeError(address + ' uses wrong encoding');
+ } else {
+ result = bech32m.decode(address);
+ version = result.words[0];
+ if (version === 0) throw new TypeError(address + ' uses wrong encoding');
+ }
+ const data = bech32.fromWords(result.words.slice(1));
+ return {
+ version,
+ prefix: result.prefix,
+ data: Uint8Array.from(data),
+ };
+}
+/**
+ * Converts a hash to a Base58Check-encoded string.
+ * @param hash - The hash to be encoded.
+ * @param version - The version byte to be prepended to the encoded string.
+ * @returns The Base58Check-encoded string.
+ */
+export function toBase58Check(hash, version) {
+ v.parse(v.tuple([Hash160bitSchema, UInt8Schema]), [hash, version]);
+ const payload = new Uint8Array(21);
+ tools.writeUInt8(payload, 0, version);
+ payload.set(hash, 1);
+ return bs58check.encode(payload);
+}
+/**
+ * Converts a buffer to a Bech32 or Bech32m encoded string.
+ * @param data - The buffer to be encoded.
+ * @param version - The version number to be used in the encoding.
+ * @param prefix - The prefix string to be used in the encoding.
+ * @returns The Bech32 or Bech32m encoded string.
+ */
+export function toBech32(data, version, prefix) {
+ const words = bech32.toWords(data);
+ words.unshift(version);
+ return version === 0
+ ? bech32.encode(prefix, words)
+ : bech32m.encode(prefix, words);
+}
+/**
+ * Converts an output script to a Bitcoin address.
+ * @param output - The output script as a Buffer.
+ * @param network - The Bitcoin network (optional).
+ * @returns The Bitcoin address corresponding to the output script.
+ * @throws If the output script has no matching address.
+ */
+export function fromOutputScript(output, network) {
+ // TODO: Network
+ network = network || networks.bitcoin;
+ try {
+ return payments.p2pkh({ output, network }).address;
+ } catch (e) {}
+ try {
+ return payments.p2sh({ output, network }).address;
+ } catch (e) {}
+ try {
+ return payments.p2wpkh({ output, network }).address;
+ } catch (e) {}
+ try {
+ return payments.p2wsh({ output, network }).address;
+ } catch (e) {}
+ try {
+ return payments.p2tr({ output, network }).address;
+ } catch (e) {}
+ try {
+ return _toFutureSegwitAddress(output, network);
+ } catch (e) {}
+ throw new Error(bscript.toASM(output) + ' has no matching Address');
+}
+/**
+ * Converts a Bitcoin address to its corresponding output script.
+ * @param address - The Bitcoin address to convert.
+ * @param network - The Bitcoin network to use. Defaults to the Bitcoin network.
+ * @returns The corresponding output script as a Buffer.
+ * @throws If the address has an invalid prefix or no matching script.
+ */
+export function toOutputScript(address, network) {
+ network = network || networks.bitcoin;
+ let decodeBase58;
+ let decodeBech32;
+ try {
+ decodeBase58 = fromBase58Check(address);
+ } catch (e) {}
+ if (decodeBase58) {
+ if (decodeBase58.version === network.pubKeyHash)
+ return payments.p2pkh({ hash: decodeBase58.hash }).output;
+ if (decodeBase58.version === network.scriptHash)
+ return payments.p2sh({ hash: decodeBase58.hash }).output;
+ } else {
+ try {
+ decodeBech32 = fromBech32(address);
+ } catch (e) {}
+ if (decodeBech32) {
+ if (decodeBech32.prefix !== network.bech32)
+ throw new Error(address + ' has an invalid prefix');
+ if (decodeBech32.version === 0) {
+ if (decodeBech32.data.length === 20)
+ return payments.p2wpkh({ hash: decodeBech32.data }).output;
+ if (decodeBech32.data.length === 32)
+ return payments.p2wsh({ hash: decodeBech32.data }).output;
+ } else if (decodeBech32.version === 1) {
+ if (decodeBech32.data.length === 32)
+ return payments.p2tr({ pubkey: decodeBech32.data }).output;
+ } else if (
+ decodeBech32.version >= FUTURE_SEGWIT_MIN_VERSION &&
+ decodeBech32.version <= FUTURE_SEGWIT_MAX_VERSION &&
+ decodeBech32.data.length >= FUTURE_SEGWIT_MIN_SIZE &&
+ decodeBech32.data.length <= FUTURE_SEGWIT_MAX_SIZE
+ ) {
+ if (WARNING_STATES[1] === false) {
+ console.warn(FUTURE_SEGWIT_VERSION_WARNING);
+ WARNING_STATES[1] = true;
+ }
+ return bscript.compile([
+ decodeBech32.version + FUTURE_SEGWIT_VERSION_DIFF,
+ decodeBech32.data,
+ ]);
+ }
+ }
+ }
+ throw new Error(address + ' has no matching Script');
+}
diff --git a/src/bip66.js b/src/esm/bip66.js
similarity index 81%
rename from src/bip66.js
rename to src/esm/bip66.js
index 0070f998c..1252a6539 100644
--- a/src/bip66.js
+++ b/src/esm/bip66.js
@@ -1,10 +1,13 @@
-'use strict';
// Reference https://github.com/bitcoin/bips/blob/master/bip-0066.mediawiki
// Format: 0x30 [total-length] 0x02 [R-length] [R] 0x02 [S-length] [S]
// NOTE: SIGHASH byte ignored AND restricted, truncate before use
-Object.defineProperty(exports, '__esModule', { value: true });
-exports.encode = exports.decode = exports.check = void 0;
-function check(buffer) {
+/**
+ * Checks if the given buffer is a valid BIP66-encoded signature.
+ *
+ * @param buffer - The buffer to check.
+ * @returns A boolean indicating whether the buffer is a valid BIP66-encoded signature.
+ */
+export function check(buffer) {
if (buffer.length < 8) return false;
if (buffer.length > 72) return false;
if (buffer[0] !== 0x30) return false;
@@ -24,8 +27,15 @@ function check(buffer) {
return false;
return true;
}
-exports.check = check;
-function decode(buffer) {
+/**
+ * Decodes a DER-encoded signature buffer and returns the R and S values.
+ * @param buffer - The DER-encoded signature buffer.
+ * @returns An object containing the R and S values.
+ * @throws {Error} If the DER sequence length is too short, too long, or invalid.
+ * @throws {Error} If the R or S length is zero or invalid.
+ * @throws {Error} If the R or S value is negative or excessively padded.
+ */
+export function decode(buffer) {
if (buffer.length < 8) throw new Error('DER sequence length is too short');
if (buffer.length > 72) throw new Error('DER sequence length is too long');
if (buffer[0] !== 0x30) throw new Error('Expected DER sequence');
@@ -51,7 +61,6 @@ function decode(buffer) {
s: buffer.slice(6 + lenR),
};
}
-exports.decode = decode;
/*
* Expects r and s to be positive DER integers.
*
@@ -74,7 +83,7 @@ exports.decode = decode;
* 62300 => 0x00f35c
* -62300 => 0xff0ca4
*/
-function encode(r, s) {
+export function encode(r, s) {
const lenR = r.length;
const lenS = s.length;
if (lenR === 0) throw new Error('R length is zero');
@@ -87,16 +96,15 @@ function encode(r, s) {
throw new Error('R value excessively padded');
if (lenS > 1 && s[0] === 0x00 && !(s[1] & 0x80))
throw new Error('S value excessively padded');
- const signature = Buffer.allocUnsafe(6 + lenR + lenS);
+ const signature = new Uint8Array(6 + lenR + lenS);
// 0x30 [total-length] 0x02 [R-length] [R] 0x02 [S-length] [S]
signature[0] = 0x30;
signature[1] = signature.length - 2;
signature[2] = 0x02;
signature[3] = r.length;
- r.copy(signature, 4);
+ signature.set(r, 4);
signature[4 + lenR] = 0x02;
signature[5 + lenR] = s.length;
- s.copy(signature, 6 + lenR);
+ signature.set(s, 6 + lenR);
return signature;
}
-exports.encode = encode;
diff --git a/src/block.js b/src/esm/block.js
similarity index 74%
rename from src/block.js
rename to src/esm/block.js
index 2c130c3bc..c717d3d7a 100644
--- a/src/block.js
+++ b/src/esm/block.js
@@ -1,32 +1,24 @@
-'use strict';
-Object.defineProperty(exports, '__esModule', { value: true });
-exports.Block = void 0;
-const bufferutils_1 = require('./bufferutils');
-const bcrypto = require('./crypto');
-const merkle_1 = require('./merkle');
-const transaction_1 = require('./transaction');
-const types = require('./types');
-const { typeforce } = types;
+import {
+ BufferReader,
+ BufferWriter,
+ reverseBuffer,
+ varuint,
+} from './bufferutils.js';
+import * as bcrypto from './crypto.js';
+import { fastMerkleRoot } from './merkle.js';
+import { Transaction } from './transaction.js';
+import * as v from 'valibot';
+import * as tools from 'uint8array-tools';
const errorMerkleNoTxes = new TypeError(
'Cannot compute merkle root for zero transactions',
);
const errorWitnessNotSegwit = new TypeError(
'Cannot compute witness commit for non-segwit block',
);
-class Block {
- constructor() {
- this.version = 1;
- this.prevHash = undefined;
- this.merkleRoot = undefined;
- this.timestamp = 0;
- this.witnessCommit = undefined;
- this.bits = 0;
- this.nonce = 0;
- this.transactions = undefined;
- }
+export class Block {
static fromBuffer(buffer) {
if (buffer.length < 80) throw new Error('Buffer too small (< 80 bytes)');
- const bufferReader = new bufferutils_1.BufferReader(buffer);
+ const bufferReader = new BufferReader(buffer);
const block = new Block();
block.version = bufferReader.readInt32();
block.prevHash = bufferReader.readSlice(32);
@@ -36,7 +28,7 @@ class Block {
block.nonce = bufferReader.readUInt32();
if (buffer.length === 80) return block;
const readTransaction = () => {
- const tx = transaction_1.Transaction.fromBuffer(
+ const tx = Transaction.fromBuffer(
bufferReader.buffer.slice(bufferReader.offset),
true,
);
@@ -55,30 +47,40 @@ class Block {
return block;
}
static fromHex(hex) {
- return Block.fromBuffer(Buffer.from(hex, 'hex'));
+ return Block.fromBuffer(tools.fromHex(hex));
}
static calculateTarget(bits) {
const exponent = ((bits & 0xff000000) >> 24) - 3;
const mantissa = bits & 0x007fffff;
- const target = Buffer.alloc(32, 0);
- target.writeUIntBE(mantissa, 29 - exponent, 3);
+ const target = new Uint8Array(32);
+ target[29 - exponent] = (mantissa >> 16) & 0xff;
+ target[30 - exponent] = (mantissa >> 8) & 0xff;
+ target[31 - exponent] = mantissa & 0xff;
return target;
}
static calculateMerkleRoot(transactions, forWitness) {
- typeforce([{ getHash: types.Function }], transactions);
+ v.parse(v.array(v.object({ getHash: v.function() })), transactions);
if (transactions.length === 0) throw errorMerkleNoTxes;
if (forWitness && !txesHaveWitnessCommit(transactions))
throw errorWitnessNotSegwit;
const hashes = transactions.map(transaction =>
transaction.getHash(forWitness),
);
- const rootHash = (0, merkle_1.fastMerkleRoot)(hashes, bcrypto.hash256);
+ const rootHash = fastMerkleRoot(hashes, bcrypto.hash256);
return forWitness
? bcrypto.hash256(
- Buffer.concat([rootHash, transactions[0].ins[0].witness[0]]),
+ tools.concat([rootHash, transactions[0].ins[0].witness[0]]),
)
: rootHash;
}
+ version = 1;
+ prevHash = undefined;
+ merkleRoot = undefined;
+ timestamp = 0;
+ witnessCommit = undefined;
+ bits = 0;
+ nonce = 0;
+ transactions = undefined;
getWitnessCommit() {
if (!txesHaveWitnessCommit(this.transactions)) return null;
// The merkle root for the witness data is in an OP_RETURN output.
@@ -86,19 +88,23 @@ class Block {
// The root is prepended with 0xaa21a9ed so check for 0x6a24aa21a9ed
// If multiple commits are found, the output with highest index is assumed.
const witnessCommits = this.transactions[0].outs
- .filter(out =>
- out.script.slice(0, 6).equals(Buffer.from('6a24aa21a9ed', 'hex')),
+ .filter(
+ out =>
+ tools.compare(
+ out.script.slice(0, 6),
+ Uint8Array.from([0x6a, 0x24, 0xaa, 0x21, 0xa9, 0xed]),
+ ) === 0,
)
.map(out => out.script.slice(6, 38));
if (witnessCommits.length === 0) return null;
// Use the commit with the highest output (should only be one though)
const result = witnessCommits[witnessCommits.length - 1];
- if (!(result instanceof Buffer && result.length === 32)) return null;
+ if (!(result instanceof Uint8Array && result.length === 32)) return null;
return result;
}
hasWitnessCommit() {
if (
- this.witnessCommit instanceof Buffer &&
+ this.witnessCommit instanceof Uint8Array &&
this.witnessCommit.length === 32
)
return true;
@@ -117,7 +123,7 @@ class Block {
if (headersOnly || !this.transactions) return 80;
return (
80 +
- bufferutils_1.varuint.encodingLength(this.transactions.length) +
+ varuint.encodingLength(this.transactions.length) +
this.transactions.reduce((a, x) => a + x.byteLength(allowWitness), 0)
);
}
@@ -125,7 +131,7 @@ class Block {
return bcrypto.hash256(this.toBuffer(true));
}
getId() {
- return (0, bufferutils_1.reverseBuffer)(this.getHash()).toString('hex');
+ return tools.toHex(reverseBuffer(this.getHash()));
}
getUTCDate() {
const date = new Date(0); // epoch
@@ -134,8 +140,8 @@ class Block {
}
// TODO: buffer, offset compatibility
toBuffer(headersOnly) {
- const buffer = Buffer.allocUnsafe(this.byteLength(headersOnly));
- const bufferWriter = new bufferutils_1.BufferWriter(buffer);
+ const buffer = new Uint8Array(this.byteLength(headersOnly));
+ const bufferWriter = new BufferWriter(buffer);
bufferWriter.writeInt32(this.version);
bufferWriter.writeSlice(this.prevHash);
bufferWriter.writeSlice(this.merkleRoot);
@@ -143,12 +149,12 @@ class Block {
bufferWriter.writeUInt32(this.bits);
bufferWriter.writeUInt32(this.nonce);
if (headersOnly || !this.transactions) return buffer;
- bufferutils_1.varuint.encode(
+ const { bytes } = varuint.encode(
this.transactions.length,
buffer,
bufferWriter.offset,
);
- bufferWriter.offset += bufferutils_1.varuint.encode.bytes;
+ bufferWriter.offset += bytes;
this.transactions.forEach(tx => {
const txSize = tx.byteLength(); // TODO: extract from toBuffer?
tx.toBuffer(buffer, bufferWriter.offset);
@@ -157,7 +163,7 @@ class Block {
return buffer;
}
toHex(headersOnly) {
- return this.toBuffer(headersOnly).toString('hex');
+ return tools.toHex(this.toBuffer(headersOnly));
}
checkTxRoots() {
// If the Block has segwit transactions but no witness commit,
@@ -170,14 +176,14 @@ class Block {
);
}
checkProofOfWork() {
- const hash = (0, bufferutils_1.reverseBuffer)(this.getHash());
+ const hash = reverseBuffer(this.getHash());
const target = Block.calculateTarget(this.bits);
- return hash.compare(target) <= 0;
+ return tools.compare(hash, target) <= 0;
}
__checkMerkleRoot() {
if (!this.transactions) throw errorMerkleNoTxes;
const actualMerkleRoot = Block.calculateMerkleRoot(this.transactions);
- return this.merkleRoot.compare(actualMerkleRoot) === 0;
+ return tools.compare(this.merkleRoot, actualMerkleRoot) === 0;
}
__checkWitnessCommit() {
if (!this.transactions) throw errorMerkleNoTxes;
@@ -186,10 +192,9 @@ class Block {
this.transactions,
true,
);
- return this.witnessCommit.compare(actualWitnessCommit) === 0;
+ return tools.compare(this.witnessCommit, actualWitnessCommit) === 0;
}
}
-exports.Block = Block;
function txesHaveWitnessCommit(transactions) {
return (
transactions instanceof Array &&
diff --git a/src/esm/bufferutils.js b/src/esm/bufferutils.js
new file mode 100644
index 000000000..fb819fbb8
--- /dev/null
+++ b/src/esm/bufferutils.js
@@ -0,0 +1,156 @@
+import * as types from './types.js';
+import * as varuint from 'varuint-bitcoin';
+import * as v from 'valibot';
+export { varuint };
+import * as tools from 'uint8array-tools';
+const MAX_JS_NUMBER = 0x001fffffffffffff;
+// https://github.com/feross/buffer/blob/master/index.js#L1127
+function verifuint(value, max) {
+ if (typeof value !== 'number' && typeof value !== 'bigint')
+ throw new Error('cannot write a non-number as a number');
+ if (value < 0 && value < BigInt(0))
+ throw new Error('specified a negative value for writing an unsigned value');
+ if (value > max && value > BigInt(max))
+ throw new Error('RangeError: value out of range');
+ if (Math.floor(Number(value)) !== Number(value))
+ throw new Error('value has a fractional component');
+}
+/**
+ * Reverses the order of bytes in a buffer.
+ * @param buffer - The buffer to reverse.
+ * @returns A new buffer with the bytes reversed.
+ */
+export function reverseBuffer(buffer) {
+ if (buffer.length < 1) return buffer;
+ let j = buffer.length - 1;
+ let tmp = 0;
+ for (let i = 0; i < buffer.length / 2; i++) {
+ tmp = buffer[i];
+ buffer[i] = buffer[j];
+ buffer[j] = tmp;
+ j--;
+ }
+ return buffer;
+}
+export function cloneBuffer(buffer) {
+ const clone = new Uint8Array(buffer.length);
+ clone.set(buffer);
+ return clone;
+}
+/**
+ * Helper class for serialization of bitcoin data types into a pre-allocated buffer.
+ */
+export class BufferWriter {
+ buffer;
+ offset;
+ static withCapacity(size) {
+ return new BufferWriter(new Uint8Array(size));
+ }
+ constructor(buffer, offset = 0) {
+ this.buffer = buffer;
+ this.offset = offset;
+ v.parse(v.tuple([types.BufferSchema, types.UInt32Schema]), [
+ buffer,
+ offset,
+ ]);
+ }
+ writeUInt8(i) {
+ this.offset = tools.writeUInt8(this.buffer, this.offset, i);
+ }
+ writeInt32(i) {
+ this.offset = tools.writeInt32(this.buffer, this.offset, i, 'LE');
+ }
+ writeInt64(i) {
+ this.offset = tools.writeInt64(this.buffer, this.offset, BigInt(i), 'LE');
+ }
+ writeUInt32(i) {
+ this.offset = tools.writeUInt32(this.buffer, this.offset, i, 'LE');
+ }
+ writeUInt64(i) {
+ this.offset = tools.writeUInt64(this.buffer, this.offset, BigInt(i), 'LE');
+ }
+ writeVarInt(i) {
+ const { bytes } = varuint.encode(i, this.buffer, this.offset);
+ this.offset += bytes;
+ }
+ writeSlice(slice) {
+ if (this.buffer.length < this.offset + slice.length) {
+ throw new Error('Cannot write slice out of bounds');
+ }
+ this.buffer.set(slice, this.offset);
+ this.offset += slice.length;
+ }
+ writeVarSlice(slice) {
+ this.writeVarInt(slice.length);
+ this.writeSlice(slice);
+ }
+ writeVector(vector) {
+ this.writeVarInt(vector.length);
+ vector.forEach(buf => this.writeVarSlice(buf));
+ }
+ end() {
+ if (this.buffer.length === this.offset) {
+ return this.buffer;
+ }
+ throw new Error(`buffer size ${this.buffer.length}, offset ${this.offset}`);
+ }
+}
+/**
+ * Helper class for reading of bitcoin data types from a buffer.
+ */
+export class BufferReader {
+ buffer;
+ offset;
+ constructor(buffer, offset = 0) {
+ this.buffer = buffer;
+ this.offset = offset;
+ v.parse(v.tuple([types.BufferSchema, types.UInt32Schema]), [
+ buffer,
+ offset,
+ ]);
+ }
+ readUInt8() {
+ const result = tools.readUInt8(this.buffer, this.offset);
+ this.offset++;
+ return result;
+ }
+ readInt32() {
+ const result = tools.readInt32(this.buffer, this.offset, 'LE');
+ this.offset += 4;
+ return result;
+ }
+ readUInt32() {
+ const result = tools.readUInt32(this.buffer, this.offset, 'LE');
+ this.offset += 4;
+ return result;
+ }
+ readInt64() {
+ const result = tools.readInt64(this.buffer, this.offset, 'LE');
+ this.offset += 8;
+ return result;
+ }
+ readVarInt() {
+ const { bigintValue, bytes } = varuint.decode(this.buffer, this.offset);
+ this.offset += bytes;
+ return bigintValue;
+ }
+ readSlice(n) {
+ verifuint(n, MAX_JS_NUMBER);
+ const num = Number(n);
+ if (this.buffer.length < this.offset + num) {
+ throw new Error('Cannot read slice out of bounds');
+ }
+ const result = this.buffer.slice(this.offset, this.offset + num);
+ this.offset += num;
+ return result;
+ }
+ readVarSlice() {
+ return this.readSlice(this.readVarInt());
+ }
+ readVector() {
+ const count = this.readVarInt();
+ const vector = [];
+ for (let i = 0; i < count; i++) vector.push(this.readVarSlice());
+ return vector;
+ }
+}
diff --git a/src/esm/crypto.js b/src/esm/crypto.js
new file mode 100644
index 000000000..8a198db02
--- /dev/null
+++ b/src/esm/crypto.js
@@ -0,0 +1,123 @@
+/**
+ * A module for hashing functions.
+ * include ripemd160、sha1、sha256、hash160、hash256、taggedHash
+ *
+ * @packageDocumentation
+ */
+import { ripemd160 } from '@noble/hashes/ripemd160';
+import { sha256 } from '@noble/hashes/sha256';
+import * as tools from 'uint8array-tools';
+export { ripemd160, sha256 };
+export { sha1 } from '@noble/hashes/sha1';
+/**
+ * Computes the HASH160 (RIPEMD-160 after SHA-256) of the given buffer.
+ *
+ * @param buffer - The input data to be hashed.
+ * @returns The HASH160 of the input buffer.
+ */
+export function hash160(buffer) {
+ return ripemd160(sha256(buffer));
+}
+/**
+ * Computes the double SHA-256 hash of the given buffer.
+ *
+ * @param buffer - The input data to be hashed.
+ * @returns The double SHA-256 hash of the input buffer.
+ */
+export function hash256(buffer) {
+ return sha256(sha256(buffer));
+}
+export const TAGS = [
+ 'BIP0340/challenge',
+ 'BIP0340/aux',
+ 'BIP0340/nonce',
+ 'TapLeaf',
+ 'TapBranch',
+ 'TapSighash',
+ 'TapTweak',
+ 'KeyAgg list',
+ 'KeyAgg coefficient',
+];
+/**
+ * A collection of tagged hash prefixes used in various BIP (Bitcoin Improvement Proposals)
+ * and Taproot-related operations. Each prefix is represented as a `Uint8Array`.
+ *
+ * @constant
+ * @type {TaggedHashPrefixes}
+ *
+ * @property {'BIP0340/challenge'} - Prefix for BIP0340 challenge.
+ * @property {'BIP0340/aux'} - Prefix for BIP0340 auxiliary data.
+ * @property {'BIP0340/nonce'} - Prefix for BIP0340 nonce.
+ * @property {TapLeaf} - Prefix for Taproot leaf.
+ * @property {TapBranch} - Prefix for Taproot branch.
+ * @property {TapSighash} - Prefix for Taproot sighash.
+ * @property {TapTweak} - Prefix for Taproot tweak.
+ * @property {'KeyAgg list'} - Prefix for key aggregation list.
+ * @property {'KeyAgg coefficient'} - Prefix for key aggregation coefficient.
+ */
+export const TAGGED_HASH_PREFIXES = {
+ 'BIP0340/challenge': Uint8Array.from([
+ 123, 181, 45, 122, 159, 239, 88, 50, 62, 177, 191, 122, 64, 125, 179, 130,
+ 210, 243, 242, 216, 27, 177, 34, 79, 73, 254, 81, 143, 109, 72, 211, 124,
+ 123, 181, 45, 122, 159, 239, 88, 50, 62, 177, 191, 122, 64, 125, 179, 130,
+ 210, 243, 242, 216, 27, 177, 34, 79, 73, 254, 81, 143, 109, 72, 211, 124,
+ ]),
+ 'BIP0340/aux': Uint8Array.from([
+ 241, 239, 78, 94, 192, 99, 202, 218, 109, 148, 202, 250, 157, 152, 126, 160,
+ 105, 38, 88, 57, 236, 193, 31, 151, 45, 119, 165, 46, 216, 193, 204, 144,
+ 241, 239, 78, 94, 192, 99, 202, 218, 109, 148, 202, 250, 157, 152, 126, 160,
+ 105, 38, 88, 57, 236, 193, 31, 151, 45, 119, 165, 46, 216, 193, 204, 144,
+ ]),
+ 'BIP0340/nonce': Uint8Array.from([
+ 7, 73, 119, 52, 167, 155, 203, 53, 91, 155, 140, 125, 3, 79, 18, 28, 244,
+ 52, 215, 62, 247, 45, 218, 25, 135, 0, 97, 251, 82, 191, 235, 47, 7, 73,
+ 119, 52, 167, 155, 203, 53, 91, 155, 140, 125, 3, 79, 18, 28, 244, 52, 215,
+ 62, 247, 45, 218, 25, 135, 0, 97, 251, 82, 191, 235, 47,
+ ]),
+ TapLeaf: Uint8Array.from([
+ 174, 234, 143, 220, 66, 8, 152, 49, 5, 115, 75, 88, 8, 29, 30, 38, 56, 211,
+ 95, 28, 181, 64, 8, 212, 211, 87, 202, 3, 190, 120, 233, 238, 174, 234, 143,
+ 220, 66, 8, 152, 49, 5, 115, 75, 88, 8, 29, 30, 38, 56, 211, 95, 28, 181,
+ 64, 8, 212, 211, 87, 202, 3, 190, 120, 233, 238,
+ ]),
+ TapBranch: Uint8Array.from([
+ 25, 65, 161, 242, 229, 110, 185, 95, 162, 169, 241, 148, 190, 92, 1, 247,
+ 33, 111, 51, 237, 130, 176, 145, 70, 52, 144, 208, 91, 245, 22, 160, 21, 25,
+ 65, 161, 242, 229, 110, 185, 95, 162, 169, 241, 148, 190, 92, 1, 247, 33,
+ 111, 51, 237, 130, 176, 145, 70, 52, 144, 208, 91, 245, 22, 160, 21,
+ ]),
+ TapSighash: Uint8Array.from([
+ 244, 10, 72, 223, 75, 42, 112, 200, 180, 146, 75, 242, 101, 70, 97, 237, 61,
+ 149, 253, 102, 163, 19, 235, 135, 35, 117, 151, 198, 40, 228, 160, 49, 244,
+ 10, 72, 223, 75, 42, 112, 200, 180, 146, 75, 242, 101, 70, 97, 237, 61, 149,
+ 253, 102, 163, 19, 235, 135, 35, 117, 151, 198, 40, 228, 160, 49,
+ ]),
+ TapTweak: Uint8Array.from([
+ 232, 15, 225, 99, 156, 156, 160, 80, 227, 175, 27, 57, 193, 67, 198, 62, 66,
+ 156, 188, 235, 21, 217, 64, 251, 181, 197, 161, 244, 175, 87, 197, 233, 232,
+ 15, 225, 99, 156, 156, 160, 80, 227, 175, 27, 57, 193, 67, 198, 62, 66, 156,
+ 188, 235, 21, 217, 64, 251, 181, 197, 161, 244, 175, 87, 197, 233,
+ ]),
+ 'KeyAgg list': Uint8Array.from([
+ 72, 28, 151, 28, 60, 11, 70, 215, 240, 178, 117, 174, 89, 141, 78, 44, 126,
+ 215, 49, 156, 89, 74, 92, 110, 199, 158, 160, 212, 153, 2, 148, 240, 72, 28,
+ 151, 28, 60, 11, 70, 215, 240, 178, 117, 174, 89, 141, 78, 44, 126, 215, 49,
+ 156, 89, 74, 92, 110, 199, 158, 160, 212, 153, 2, 148, 240,
+ ]),
+ 'KeyAgg coefficient': Uint8Array.from([
+ 191, 201, 4, 3, 77, 28, 136, 232, 200, 14, 34, 229, 61, 36, 86, 109, 100,
+ 130, 78, 214, 66, 114, 129, 192, 145, 0, 249, 77, 205, 82, 201, 129, 191,
+ 201, 4, 3, 77, 28, 136, 232, 200, 14, 34, 229, 61, 36, 86, 109, 100, 130,
+ 78, 214, 66, 114, 129, 192, 145, 0, 249, 77, 205, 82, 201, 129,
+ ]),
+};
+/**
+ * Computes a tagged hash using the specified prefix and data.
+ *
+ * @param prefix - The prefix to use for the tagged hash. This should be one of the values from the `TaggedHashPrefix` enum.
+ * @param data - The data to hash, provided as a `Uint8Array`.
+ * @returns The resulting tagged hash as a `Uint8Array`.
+ */
+export function taggedHash(prefix, data) {
+ return sha256(tools.concat([TAGGED_HASH_PREFIXES[prefix], data]));
+}
diff --git a/src/esm/ecc_lib.js b/src/esm/ecc_lib.js
new file mode 100644
index 000000000..fa2b54ebe
--- /dev/null
+++ b/src/esm/ecc_lib.js
@@ -0,0 +1,108 @@
+import * as tools from 'uint8array-tools';
+const _ECCLIB_CACHE = {};
+/**
+ * Initializes the ECC library with the provided instance.
+ * If `eccLib` is `undefined`, the library will be cleared.
+ * If `eccLib` is a new instance, it will be verified before setting it as the active library.
+ *
+ * @param eccLib The instance of the ECC library to initialize.
+ * @param opts Extra initialization options. Use {DANGER_DO_NOT_VERIFY_ECCLIB:true} if ecc verification should not be executed. Not recommended!
+ */
+export function initEccLib(eccLib, opts) {
+ if (!eccLib) {
+ // allow clearing the library
+ _ECCLIB_CACHE.eccLib = eccLib;
+ } else if (eccLib !== _ECCLIB_CACHE.eccLib) {
+ if (!opts?.DANGER_DO_NOT_VERIFY_ECCLIB)
+ // new instance, verify it
+ verifyEcc(eccLib);
+ _ECCLIB_CACHE.eccLib = eccLib;
+ }
+}
+/**
+ * Retrieves the ECC Library instance.
+ * Throws an error if the ECC Library is not provided.
+ * You must call initEccLib() with a valid TinySecp256k1Interface instance before calling this function.
+ * @returns The ECC Library instance.
+ * @throws Error if the ECC Library is not provided.
+ */
+export function getEccLib() {
+ if (!_ECCLIB_CACHE.eccLib)
+ throw new Error(
+ 'No ECC Library provided. You must call initEccLib() with a valid TinySecp256k1Interface instance',
+ );
+ return _ECCLIB_CACHE.eccLib;
+}
+const h = hex => tools.fromHex(hex);
+/**
+ * Verifies the ECC functionality.
+ *
+ * @param ecc - The TinySecp256k1Interface object.
+ */
+function verifyEcc(ecc) {
+ assert(typeof ecc.isXOnlyPoint === 'function');
+ assert(
+ ecc.isXOnlyPoint(
+ h('79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798'),
+ ),
+ );
+ assert(
+ ecc.isXOnlyPoint(
+ h('fffffffffffffffffffffffffffffffffffffffffffffffffffffffeeffffc2e'),
+ ),
+ );
+ assert(
+ ecc.isXOnlyPoint(
+ h('f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9'),
+ ),
+ );
+ assert(
+ ecc.isXOnlyPoint(
+ h('0000000000000000000000000000000000000000000000000000000000000001'),
+ ),
+ );
+ assert(
+ !ecc.isXOnlyPoint(
+ h('0000000000000000000000000000000000000000000000000000000000000000'),
+ ),
+ );
+ assert(
+ !ecc.isXOnlyPoint(
+ h('fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f'),
+ ),
+ );
+ assert(typeof ecc.xOnlyPointAddTweak === 'function');
+ tweakAddVectors.forEach(t => {
+ const r = ecc.xOnlyPointAddTweak(h(t.pubkey), h(t.tweak));
+ if (t.result === null) {
+ assert(r === null);
+ } else {
+ assert(r !== null);
+ assert(r.parity === t.parity);
+ assert(tools.compare(r.xOnlyPubkey, h(t.result)) === 0);
+ }
+ });
+}
+function assert(bool) {
+ if (!bool) throw new Error('ecc library invalid');
+}
+const tweakAddVectors = [
+ {
+ pubkey: '79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798',
+ tweak: 'fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140',
+ parity: -1,
+ result: null,
+ },
+ {
+ pubkey: '1617d38ed8d8657da4d4761e8057bc396ea9e4b9d29776d4be096016dbd2509b',
+ tweak: 'a8397a935f0dfceba6ba9618f6451ef4d80637abf4e6af2669fbc9de6a8fd2ac',
+ parity: 1,
+ result: 'e478f99dab91052ab39a33ea35fd5e6e4933f4d28023cd597c9a1f6760346adf',
+ },
+ {
+ pubkey: '2c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e668680991',
+ tweak: '823c3cd2142744b075a87eade7e1b8678ba308d566226a0056ca2b7a76f86b47',
+ parity: 0,
+ result: '9534f8dc8c6deda2dc007655981c78b49c5d96c778fbf363462a11ec9dfd948c',
+ },
+];
diff --git a/src/esm/index.js b/src/esm/index.js
new file mode 100644
index 000000000..0b87f3023
--- /dev/null
+++ b/src/esm/index.js
@@ -0,0 +1,12 @@
+import * as address from './address.js';
+import * as crypto from './crypto.js';
+import * as networks from './networks.js';
+import * as payments from './payments/index.js';
+import * as script from './script.js';
+export { address, crypto, networks, payments, script };
+export { Block } from './block.js';
+export { Psbt, toXOnly } from './psbt.js';
+/** @hidden */
+export { OPS as opcodes } from './ops.js';
+export { Transaction } from './transaction.js';
+export { initEccLib } from './ecc_lib.js';
diff --git a/src/esm/merkle.js b/src/esm/merkle.js
new file mode 100644
index 000000000..0a67870c0
--- /dev/null
+++ b/src/esm/merkle.js
@@ -0,0 +1,27 @@
+import * as tools from 'uint8array-tools';
+/**
+ * Calculates the Merkle root of an array of buffers using a specified digest function.
+ *
+ * @param values - The array of buffers.
+ * @param digestFn - The digest function used to calculate the hash of the concatenated buffers.
+ * @returns The Merkle root as a buffer.
+ * @throws {TypeError} If the values parameter is not an array or the digestFn parameter is not a function.
+ */
+export function fastMerkleRoot(values, digestFn) {
+ if (!Array.isArray(values)) throw TypeError('Expected values Array');
+ if (typeof digestFn !== 'function')
+ throw TypeError('Expected digest Function');
+ let length = values.length;
+ const results = values.concat();
+ while (length > 1) {
+ let j = 0;
+ for (let i = 0; i < length; i += 2, ++j) {
+ const left = results[i];
+ const right = i + 1 === length ? left : results[i + 1];
+ const data = tools.concat([left, right]);
+ results[j] = digestFn(data);
+ }
+ length = j;
+ }
+ return results[0];
+}
diff --git a/src/esm/networks.js b/src/esm/networks.js
new file mode 100644
index 000000000..f5620971d
--- /dev/null
+++ b/src/esm/networks.js
@@ -0,0 +1,66 @@
+/**
+ * Represents the Bitcoin network configuration.
+ */
+export const bitcoin = {
+ /**
+ * The message prefix used for signing Bitcoin messages.
+ */
+ messagePrefix: '\x18Bitcoin Signed Message:\n',
+ /**
+ * The Bech32 prefix used for Bitcoin addresses.
+ */
+ bech32: 'bc',
+ /**
+ * The BIP32 key prefixes for Bitcoin.
+ */
+ bip32: {
+ /**
+ * The public key prefix for BIP32 extended public keys.
+ */
+ public: 0x0488b21e,
+ /**
+ * The private key prefix for BIP32 extended private keys.
+ */
+ private: 0x0488ade4,
+ },
+ /**
+ * The prefix for Bitcoin public key hashes.
+ */
+ pubKeyHash: 0x00,
+ /**
+ * The prefix for Bitcoin script hashes.
+ */
+ scriptHash: 0x05,
+ /**
+ * The prefix for Bitcoin Wallet Import Format (WIF) private keys.
+ */
+ wif: 0x80,
+};
+/**
+ * Represents the regtest network configuration.
+ */
+export const regtest = {
+ messagePrefix: '\x18Bitcoin Signed Message:\n',
+ bech32: 'bcrt',
+ bip32: {
+ public: 0x043587cf,
+ private: 0x04358394,
+ },
+ pubKeyHash: 0x6f,
+ scriptHash: 0xc4,
+ wif: 0xef,
+};
+/**
+ * Represents the testnet network configuration.
+ */
+export const testnet = {
+ messagePrefix: '\x18Bitcoin Signed Message:\n',
+ bech32: 'tb',
+ bip32: {
+ public: 0x043587cf,
+ private: 0x04358394,
+ },
+ pubKeyHash: 0x6f,
+ scriptHash: 0xc4,
+ wif: 0xef,
+};
diff --git a/src/esm/ops.js b/src/esm/ops.js
new file mode 100644
index 000000000..147c10961
--- /dev/null
+++ b/src/esm/ops.js
@@ -0,0 +1,125 @@
+// Define OPS enum
+var OPS;
+(function (OPS) {
+ OPS[(OPS['OP_FALSE'] = 0)] = 'OP_FALSE';
+ OPS[(OPS['OP_0'] = 0)] = 'OP_0';
+ OPS[(OPS['OP_PUSHDATA1'] = 76)] = 'OP_PUSHDATA1';
+ OPS[(OPS['OP_PUSHDATA2'] = 77)] = 'OP_PUSHDATA2';
+ OPS[(OPS['OP_PUSHDATA4'] = 78)] = 'OP_PUSHDATA4';
+ OPS[(OPS['OP_1NEGATE'] = 79)] = 'OP_1NEGATE';
+ OPS[(OPS['OP_RESERVED'] = 80)] = 'OP_RESERVED';
+ OPS[(OPS['OP_TRUE'] = 81)] = 'OP_TRUE';
+ OPS[(OPS['OP_1'] = 81)] = 'OP_1';
+ OPS[(OPS['OP_2'] = 82)] = 'OP_2';
+ OPS[(OPS['OP_3'] = 83)] = 'OP_3';
+ OPS[(OPS['OP_4'] = 84)] = 'OP_4';
+ OPS[(OPS['OP_5'] = 85)] = 'OP_5';
+ OPS[(OPS['OP_6'] = 86)] = 'OP_6';
+ OPS[(OPS['OP_7'] = 87)] = 'OP_7';
+ OPS[(OPS['OP_8'] = 88)] = 'OP_8';
+ OPS[(OPS['OP_9'] = 89)] = 'OP_9';
+ OPS[(OPS['OP_10'] = 90)] = 'OP_10';
+ OPS[(OPS['OP_11'] = 91)] = 'OP_11';
+ OPS[(OPS['OP_12'] = 92)] = 'OP_12';
+ OPS[(OPS['OP_13'] = 93)] = 'OP_13';
+ OPS[(OPS['OP_14'] = 94)] = 'OP_14';
+ OPS[(OPS['OP_15'] = 95)] = 'OP_15';
+ OPS[(OPS['OP_16'] = 96)] = 'OP_16';
+ OPS[(OPS['OP_NOP'] = 97)] = 'OP_NOP';
+ OPS[(OPS['OP_VER'] = 98)] = 'OP_VER';
+ OPS[(OPS['OP_IF'] = 99)] = 'OP_IF';
+ OPS[(OPS['OP_NOTIF'] = 100)] = 'OP_NOTIF';
+ OPS[(OPS['OP_VERIF'] = 101)] = 'OP_VERIF';
+ OPS[(OPS['OP_VERNOTIF'] = 102)] = 'OP_VERNOTIF';
+ OPS[(OPS['OP_ELSE'] = 103)] = 'OP_ELSE';
+ OPS[(OPS['OP_ENDIF'] = 104)] = 'OP_ENDIF';
+ OPS[(OPS['OP_VERIFY'] = 105)] = 'OP_VERIFY';
+ OPS[(OPS['OP_RETURN'] = 106)] = 'OP_RETURN';
+ OPS[(OPS['OP_TOALTSTACK'] = 107)] = 'OP_TOALTSTACK';
+ OPS[(OPS['OP_FROMALTSTACK'] = 108)] = 'OP_FROMALTSTACK';
+ OPS[(OPS['OP_2DROP'] = 109)] = 'OP_2DROP';
+ OPS[(OPS['OP_2DUP'] = 110)] = 'OP_2DUP';
+ OPS[(OPS['OP_3DUP'] = 111)] = 'OP_3DUP';
+ OPS[(OPS['OP_2OVER'] = 112)] = 'OP_2OVER';
+ OPS[(OPS['OP_2ROT'] = 113)] = 'OP_2ROT';
+ OPS[(OPS['OP_2SWAP'] = 114)] = 'OP_2SWAP';
+ OPS[(OPS['OP_IFDUP'] = 115)] = 'OP_IFDUP';
+ OPS[(OPS['OP_DEPTH'] = 116)] = 'OP_DEPTH';
+ OPS[(OPS['OP_DROP'] = 117)] = 'OP_DROP';
+ OPS[(OPS['OP_DUP'] = 118)] = 'OP_DUP';
+ OPS[(OPS['OP_NIP'] = 119)] = 'OP_NIP';
+ OPS[(OPS['OP_OVER'] = 120)] = 'OP_OVER';
+ OPS[(OPS['OP_PICK'] = 121)] = 'OP_PICK';
+ OPS[(OPS['OP_ROLL'] = 122)] = 'OP_ROLL';
+ OPS[(OPS['OP_ROT'] = 123)] = 'OP_ROT';
+ OPS[(OPS['OP_SWAP'] = 124)] = 'OP_SWAP';
+ OPS[(OPS['OP_TUCK'] = 125)] = 'OP_TUCK';
+ OPS[(OPS['OP_CAT'] = 126)] = 'OP_CAT';
+ OPS[(OPS['OP_SUBSTR'] = 127)] = 'OP_SUBSTR';
+ OPS[(OPS['OP_LEFT'] = 128)] = 'OP_LEFT';
+ OPS[(OPS['OP_RIGHT'] = 129)] = 'OP_RIGHT';
+ OPS[(OPS['OP_SIZE'] = 130)] = 'OP_SIZE';
+ OPS[(OPS['OP_INVERT'] = 131)] = 'OP_INVERT';
+ OPS[(OPS['OP_AND'] = 132)] = 'OP_AND';
+ OPS[(OPS['OP_OR'] = 133)] = 'OP_OR';
+ OPS[(OPS['OP_XOR'] = 134)] = 'OP_XOR';
+ OPS[(OPS['OP_EQUAL'] = 135)] = 'OP_EQUAL';
+ OPS[(OPS['OP_EQUALVERIFY'] = 136)] = 'OP_EQUALVERIFY';
+ OPS[(OPS['OP_RESERVED1'] = 137)] = 'OP_RESERVED1';
+ OPS[(OPS['OP_RESERVED2'] = 138)] = 'OP_RESERVED2';
+ OPS[(OPS['OP_1ADD'] = 139)] = 'OP_1ADD';
+ OPS[(OPS['OP_1SUB'] = 140)] = 'OP_1SUB';
+ OPS[(OPS['OP_2MUL'] = 141)] = 'OP_2MUL';
+ OPS[(OPS['OP_2DIV'] = 142)] = 'OP_2DIV';
+ OPS[(OPS['OP_NEGATE'] = 143)] = 'OP_NEGATE';
+ OPS[(OPS['OP_ABS'] = 144)] = 'OP_ABS';
+ OPS[(OPS['OP_NOT'] = 145)] = 'OP_NOT';
+ OPS[(OPS['OP_0NOTEQUAL'] = 146)] = 'OP_0NOTEQUAL';
+ OPS[(OPS['OP_ADD'] = 147)] = 'OP_ADD';
+ OPS[(OPS['OP_SUB'] = 148)] = 'OP_SUB';
+ OPS[(OPS['OP_MUL'] = 149)] = 'OP_MUL';
+ OPS[(OPS['OP_DIV'] = 150)] = 'OP_DIV';
+ OPS[(OPS['OP_MOD'] = 151)] = 'OP_MOD';
+ OPS[(OPS['OP_LSHIFT'] = 152)] = 'OP_LSHIFT';
+ OPS[(OPS['OP_RSHIFT'] = 153)] = 'OP_RSHIFT';
+ OPS[(OPS['OP_BOOLAND'] = 154)] = 'OP_BOOLAND';
+ OPS[(OPS['OP_BOOLOR'] = 155)] = 'OP_BOOLOR';
+ OPS[(OPS['OP_NUMEQUAL'] = 156)] = 'OP_NUMEQUAL';
+ OPS[(OPS['OP_NUMEQUALVERIFY'] = 157)] = 'OP_NUMEQUALVERIFY';
+ OPS[(OPS['OP_NUMNOTEQUAL'] = 158)] = 'OP_NUMNOTEQUAL';
+ OPS[(OPS['OP_LESSTHAN'] = 159)] = 'OP_LESSTHAN';
+ OPS[(OPS['OP_GREATERTHAN'] = 160)] = 'OP_GREATERTHAN';
+ OPS[(OPS['OP_LESSTHANOREQUAL'] = 161)] = 'OP_LESSTHANOREQUAL';
+ OPS[(OPS['OP_GREATERTHANOREQUAL'] = 162)] = 'OP_GREATERTHANOREQUAL';
+ OPS[(OPS['OP_MIN'] = 163)] = 'OP_MIN';
+ OPS[(OPS['OP_MAX'] = 164)] = 'OP_MAX';
+ OPS[(OPS['OP_WITHIN'] = 165)] = 'OP_WITHIN';
+ OPS[(OPS['OP_RIPEMD160'] = 166)] = 'OP_RIPEMD160';
+ OPS[(OPS['OP_SHA1'] = 167)] = 'OP_SHA1';
+ OPS[(OPS['OP_SHA256'] = 168)] = 'OP_SHA256';
+ OPS[(OPS['OP_HASH160'] = 169)] = 'OP_HASH160';
+ OPS[(OPS['OP_HASH256'] = 170)] = 'OP_HASH256';
+ OPS[(OPS['OP_CODESEPARATOR'] = 171)] = 'OP_CODESEPARATOR';
+ OPS[(OPS['OP_CHECKSIG'] = 172)] = 'OP_CHECKSIG';
+ OPS[(OPS['OP_CHECKSIGVERIFY'] = 173)] = 'OP_CHECKSIGVERIFY';
+ OPS[(OPS['OP_CHECKMULTISIG'] = 174)] = 'OP_CHECKMULTISIG';
+ OPS[(OPS['OP_CHECKMULTISIGVERIFY'] = 175)] = 'OP_CHECKMULTISIGVERIFY';
+ OPS[(OPS['OP_NOP1'] = 176)] = 'OP_NOP1';
+ OPS[(OPS['OP_CHECKLOCKTIMEVERIFY'] = 177)] = 'OP_CHECKLOCKTIMEVERIFY';
+ OPS[(OPS['OP_NOP2'] = 177)] = 'OP_NOP2';
+ OPS[(OPS['OP_CHECKSEQUENCEVERIFY'] = 178)] = 'OP_CHECKSEQUENCEVERIFY';
+ OPS[(OPS['OP_NOP3'] = 178)] = 'OP_NOP3';
+ OPS[(OPS['OP_NOP4'] = 179)] = 'OP_NOP4';
+ OPS[(OPS['OP_NOP5'] = 180)] = 'OP_NOP5';
+ OPS[(OPS['OP_NOP6'] = 181)] = 'OP_NOP6';
+ OPS[(OPS['OP_NOP7'] = 182)] = 'OP_NOP7';
+ OPS[(OPS['OP_NOP8'] = 183)] = 'OP_NOP8';
+ OPS[(OPS['OP_NOP9'] = 184)] = 'OP_NOP9';
+ OPS[(OPS['OP_NOP10'] = 185)] = 'OP_NOP10';
+ OPS[(OPS['OP_CHECKSIGADD'] = 186)] = 'OP_CHECKSIGADD';
+ OPS[(OPS['OP_PUBKEYHASH'] = 253)] = 'OP_PUBKEYHASH';
+ OPS[(OPS['OP_PUBKEY'] = 254)] = 'OP_PUBKEY';
+ OPS[(OPS['OP_INVALIDOPCODE'] = 255)] = 'OP_INVALIDOPCODE';
+})(OPS || (OPS = {}));
+// Export modules
+export { OPS };
diff --git a/src/esm/payments/bip341.js b/src/esm/payments/bip341.js
new file mode 100644
index 000000000..2317358b1
--- /dev/null
+++ b/src/esm/payments/bip341.js
@@ -0,0 +1,135 @@
+import { getEccLib } from '../ecc_lib.js';
+import * as bcrypto from '../crypto.js';
+import { varuint } from '../bufferutils.js';
+import { isTapleaf } from '../types.js';
+import * as tools from 'uint8array-tools';
+export const LEAF_VERSION_TAPSCRIPT = 0xc0;
+export const MAX_TAPTREE_DEPTH = 128;
+const isHashBranch = ht => 'left' in ht && 'right' in ht;
+/**
+ * Calculates the root hash from a given control block and leaf hash.
+ * @param controlBlock - The control block buffer.
+ * @param leafHash - The leaf hash buffer.
+ * @returns The root hash buffer.
+ * @throws {TypeError} If the control block length is less than 33.
+ */
+export function rootHashFromPath(controlBlock, leafHash) {
+ if (controlBlock.length < 33)
+ throw new TypeError(
+ `The control-block length is too small. Got ${controlBlock.length}, expected min 33.`,
+ );
+ const m = (controlBlock.length - 33) / 32;
+ let kj = leafHash;
+ for (let j = 0; j < m; j++) {
+ const ej = controlBlock.slice(33 + 32 * j, 65 + 32 * j);
+ if (tools.compare(kj, ej) < 0) {
+ kj = tapBranchHash(kj, ej);
+ } else {
+ kj = tapBranchHash(ej, kj);
+ }
+ }
+ return kj;
+}
+/**
+ * Build a hash tree of merkle nodes from the scripts binary tree.
+ * @param scriptTree - the tree of scripts to pairwise hash.
+ */
+export function toHashTree(scriptTree) {
+ if (isTapleaf(scriptTree)) return { hash: tapleafHash(scriptTree) };
+ const hashes = [toHashTree(scriptTree[0]), toHashTree(scriptTree[1])];
+ // hashes.sort((a, b) => a.hash.compare(b.hash));
+ hashes.sort((a, b) => tools.compare(a.hash, b.hash));
+ const [left, right] = hashes;
+ return {
+ hash: tapBranchHash(left.hash, right.hash),
+ left,
+ right,
+ };
+}
+/**
+ * Given a HashTree, finds the path from a particular hash to the root.
+ * @param node - the root of the tree
+ * @param hash - the hash to search for
+ * @returns - array of sibling hashes, from leaf (inclusive) to root
+ * (exclusive) needed to prove inclusion of the specified hash. undefined if no
+ * path is found
+ */
+export function findScriptPath(node, hash) {
+ if (isHashBranch(node)) {
+ const leftPath = findScriptPath(node.left, hash);
+ if (leftPath !== undefined) return [...leftPath, node.right.hash];
+ const rightPath = findScriptPath(node.right, hash);
+ if (rightPath !== undefined) return [...rightPath, node.left.hash];
+ } else if (tools.compare(node.hash, hash) === 0) {
+ return [];
+ }
+ return undefined;
+}
+/**
+ * Calculates the tapleaf hash for a given Tapleaf object.
+ * @param leaf - The Tapleaf object to calculate the hash for.
+ * @returns The tapleaf hash as a Buffer.
+ */
+export function tapleafHash(leaf) {
+ const version = leaf.version || LEAF_VERSION_TAPSCRIPT;
+ return bcrypto.taggedHash(
+ 'TapLeaf',
+ tools.concat([Uint8Array.from([version]), serializeScript(leaf.output)]),
+ );
+}
+/**
+ * Computes the taproot tweak hash for a given public key and optional hash.
+ * If a hash is provided, the public key and hash are concatenated before computing the hash.
+ * If no hash is provided, only the public key is used to compute the hash.
+ *
+ * @param pubKey - The public key buffer.
+ * @param h - The optional hash buffer.
+ * @returns The taproot tweak hash.
+ */
+export function tapTweakHash(pubKey, h) {
+ return bcrypto.taggedHash(
+ 'TapTweak',
+ tools.concat(h ? [pubKey, h] : [pubKey]),
+ );
+}
+/**
+ * Tweak a public key with a given tweak hash.
+ * @param pubKey - The public key to be tweaked.
+ * @param h - The tweak hash.
+ * @returns The tweaked public key or null if the input is invalid.
+ */
+export function tweakKey(pubKey, h) {
+ if (!(pubKey instanceof Uint8Array)) return null;
+ if (pubKey.length !== 32) return null;
+ if (h && h.length !== 32) return null;
+ const tweakHash = tapTweakHash(pubKey, h);
+ const res = getEccLib().xOnlyPointAddTweak(pubKey, tweakHash);
+ if (!res || res.xOnlyPubkey === null) return null;
+ return {
+ parity: res.parity,
+ x: Uint8Array.from(res.xOnlyPubkey),
+ };
+}
+/**
+ * Computes the TapBranch hash by concatenating two buffers and applying the 'TapBranch' tagged hash algorithm.
+ *
+ * @param a - The first buffer.
+ * @param b - The second buffer.
+ * @returns The TapBranch hash of the concatenated buffers.
+ */
+function tapBranchHash(a, b) {
+ return bcrypto.taggedHash('TapBranch', tools.concat([a, b]));
+}
+/**
+ * Serializes a script by encoding its length as a varint and concatenating it with the script.
+ *
+ * @param s - The script to be serialized.
+ * @returns The serialized script as a Buffer.
+ */
+function serializeScript(s) {
+ /* global BigInt */
+ const varintLen = varuint.encodingLength(s.length);
+ const buffer = new Uint8Array(varintLen);
+ varuint.encode(s.length, buffer);
+ return tools.concat([buffer, s]);
+}
diff --git a/src/payments/embed.js b/src/esm/payments/embed.js
similarity index 50%
rename from src/payments/embed.js
rename to src/esm/payments/embed.js
index 4b7218f36..d16db9c93 100644
--- a/src/payments/embed.js
+++ b/src/esm/payments/embed.js
@@ -1,32 +1,31 @@
-'use strict';
-Object.defineProperty(exports, '__esModule', { value: true });
-exports.p2data = void 0;
-const networks_1 = require('../networks');
-const bscript = require('../script');
-const types_1 = require('../types');
-const lazy = require('./lazy');
+import { bitcoin as BITCOIN_NETWORK } from '../networks.js';
+import * as bscript from '../script.js';
+import { stacksEqual, BufferSchema } from '../types.js';
+import * as lazy from './lazy.js';
+import * as v from 'valibot';
const OPS = bscript.OPS;
-function stacksEqual(a, b) {
- if (a.length !== b.length) return false;
- return a.every((x, i) => {
- return x.equals(b[i]);
- });
-}
// output: OP_RETURN ...
-function p2data(a, opts) {
+/**
+ * Embeds data in a Bitcoin payment.
+ * @param a - The payment object.
+ * @param opts - Optional payment options.
+ * @returns The modified payment object.
+ * @throws {TypeError} If there is not enough data or if the output is invalid.
+ */
+export function p2data(a, opts) {
if (!a.data && !a.output) throw new TypeError('Not enough data');
opts = Object.assign({ validate: true }, opts || {});
- (0, types_1.typeforce)(
- {
- network: types_1.typeforce.maybe(types_1.typeforce.Object),
- output: types_1.typeforce.maybe(types_1.typeforce.Buffer),
- data: types_1.typeforce.maybe(
- types_1.typeforce.arrayOf(types_1.typeforce.Buffer),
- ),
- },
+ v.parse(
+ v.partial(
+ v.object({
+ network: v.object({}),
+ output: BufferSchema,
+ data: v.array(BufferSchema),
+ }),
+ ),
a,
);
- const network = a.network || networks_1.bitcoin;
+ const network = a.network || BITCOIN_NETWORK;
const o = { name: 'embed', network };
lazy.prop(o, 'output', () => {
if (!a.data) return;
@@ -41,7 +40,7 @@ function p2data(a, opts) {
if (a.output) {
const chunks = bscript.decompile(a.output);
if (chunks[0] !== OPS.OP_RETURN) throw new TypeError('Output is invalid');
- if (!chunks.slice(1).every(types_1.typeforce.Buffer))
+ if (!chunks.slice(1).every(chunk => v.is(BufferSchema, chunk)))
throw new TypeError('Output is invalid');
if (a.data && !stacksEqual(a.data, o.data))
throw new TypeError('Data mismatch');
@@ -49,4 +48,3 @@ function p2data(a, opts) {
}
return Object.assign(o, a);
}
-exports.p2data = p2data;
diff --git a/src/esm/payments/index.js b/src/esm/payments/index.js
new file mode 100644
index 000000000..3c2fd1e5e
--- /dev/null
+++ b/src/esm/payments/index.js
@@ -0,0 +1,11 @@
+import { p2data as embed } from './embed.js';
+import { p2ms } from './p2ms.js';
+import { p2pk } from './p2pk.js';
+import { p2pkh } from './p2pkh.js';
+import { p2sh } from './p2sh.js';
+import { p2wpkh } from './p2wpkh.js';
+import { p2wsh } from './p2wsh.js';
+import { p2tr } from './p2tr.js';
+export { embed, p2ms, p2pk, p2pkh, p2sh, p2wpkh, p2wsh, p2tr };
+// TODO
+// witness commitment
diff --git a/src/esm/payments/lazy.js b/src/esm/payments/lazy.js
new file mode 100644
index 000000000..cc66c9163
--- /dev/null
+++ b/src/esm/payments/lazy.js
@@ -0,0 +1,27 @@
+export function prop(object, name, f) {
+ Object.defineProperty(object, name, {
+ configurable: true,
+ enumerable: true,
+ get() {
+ const _value = f.call(this);
+ this[name] = _value;
+ return _value;
+ },
+ set(_value) {
+ Object.defineProperty(this, name, {
+ configurable: true,
+ enumerable: true,
+ value: _value,
+ writable: true,
+ });
+ },
+ });
+}
+export function value(f) {
+ let _value;
+ return () => {
+ if (_value !== undefined) return _value;
+ _value = f();
+ return _value;
+ };
+}
diff --git a/src/payments/p2ms.js b/src/esm/payments/p2ms.js
similarity index 62%
rename from src/payments/p2ms.js
rename to src/esm/payments/p2ms.js
index 0b7e72d9a..2d82e510e 100644
--- a/src/payments/p2ms.js
+++ b/src/esm/payments/p2ms.js
@@ -1,21 +1,37 @@
-'use strict';
-Object.defineProperty(exports, '__esModule', { value: true });
-exports.p2ms = void 0;
-const networks_1 = require('../networks');
-const bscript = require('../script');
-const types_1 = require('../types');
-const lazy = require('./lazy');
+import { bitcoin as BITCOIN_NETWORK } from '../networks.js';
+import * as bscript from '../script.js';
+import * as scriptNumber from '../script_number.js';
+import { BufferSchema, isPoint, stacksEqual } from '../types.js';
+import * as lazy from './lazy.js';
+import * as v from 'valibot';
const OPS = bscript.OPS;
const OP_INT_BASE = OPS.OP_RESERVED; // OP_1 - 1
-function stacksEqual(a, b) {
- if (a.length !== b.length) return false;
- return a.every((x, i) => {
- return x.equals(b[i]);
- });
+function encodeSmallOrScriptNum(n) {
+ return n <= 16 ? OP_INT_BASE + n : scriptNumber.encode(n);
+}
+function decodeSmallOrScriptNum(chunk) {
+ if (typeof chunk === 'number') {
+ const val = chunk - OP_INT_BASE;
+ if (val < 1 || val > 16)
+ throw new TypeError(`Invalid opcode: expected OP_1–OP_16, got ${chunk}`);
+ return val;
+ } else return scriptNumber.decode(chunk);
+}
+function isSmallOrScriptNum(chunk) {
+ if (typeof chunk === 'number')
+ return chunk - OP_INT_BASE >= 1 && chunk - OP_INT_BASE <= 16;
+ else return Number.isInteger(scriptNumber.decode(chunk));
}
// input: OP_0 [signatures ...]
// output: m [pubKeys ...] n OP_CHECKMULTISIG
-function p2ms(a, opts) {
+/**
+ * Represents a function that creates a Pay-to-Multisig (P2MS) payment object.
+ * @param a - The payment object.
+ * @param opts - Optional payment options.
+ * @returns The created payment object.
+ * @throws {TypeError} If the provided data is not valid.
+ */
+export function p2ms(a, opts) {
if (
!a.input &&
!a.output &&
@@ -30,23 +46,24 @@ function p2ms(a, opts) {
(opts.allowIncomplete && x === OPS.OP_0) !== undefined
);
}
- (0, types_1.typeforce)(
- {
- network: types_1.typeforce.maybe(types_1.typeforce.Object),
- m: types_1.typeforce.maybe(types_1.typeforce.Number),
- n: types_1.typeforce.maybe(types_1.typeforce.Number),
- output: types_1.typeforce.maybe(types_1.typeforce.Buffer),
- pubkeys: types_1.typeforce.maybe(
- types_1.typeforce.arrayOf(types_1.isPoint),
- ),
- signatures: types_1.typeforce.maybe(
- types_1.typeforce.arrayOf(isAcceptableSignature),
- ),
- input: types_1.typeforce.maybe(types_1.typeforce.Buffer),
- },
+ v.parse(
+ v.partial(
+ v.object({
+ network: v.object({}),
+ m: v.number(),
+ n: v.number(),
+ output: BufferSchema,
+ pubkeys: v.array(v.custom(isPoint), 'Received invalid pubkey'),
+ signatures: v.array(
+ v.custom(isAcceptableSignature),
+ 'Expected signature to be of type isAcceptableSignature',
+ ),
+ input: BufferSchema,
+ }),
+ ),
a,
);
- const network = a.network || networks_1.bitcoin;
+ const network = a.network || BITCOIN_NETWORK;
const o = { network };
let chunks = [];
let decoded = false;
@@ -54,8 +71,9 @@ function p2ms(a, opts) {
if (decoded) return;
decoded = true;
chunks = bscript.decompile(output);
- o.m = chunks[0] - OP_INT_BASE;
- o.n = chunks[chunks.length - 2] - OP_INT_BASE;
+ if (chunks.length < 3) throw new TypeError('Output is invalid');
+ o.m = decodeSmallOrScriptNum(chunks[0]);
+ o.n = decodeSmallOrScriptNum(chunks[chunks.length - 2]);
o.pubkeys = chunks.slice(1, -2);
}
lazy.prop(o, 'output', () => {
@@ -64,9 +82,9 @@ function p2ms(a, opts) {
if (!a.pubkeys) return;
return bscript.compile(
[].concat(
- OP_INT_BASE + a.m,
+ encodeSmallOrScriptNum(a.m),
a.pubkeys,
- OP_INT_BASE + o.n,
+ encodeSmallOrScriptNum(o.n),
OPS.OP_CHECKMULTISIG,
),
);
@@ -105,15 +123,15 @@ function p2ms(a, opts) {
if (opts.validate) {
if (a.output) {
decode(a.output);
- if (!types_1.typeforce.Number(chunks[0]))
+ if (!isSmallOrScriptNum(chunks[0]))
throw new TypeError('Output is invalid');
- if (!types_1.typeforce.Number(chunks[chunks.length - 2]))
+ if (!isSmallOrScriptNum(chunks[chunks.length - 2]))
throw new TypeError('Output is invalid');
if (chunks[chunks.length - 1] !== OPS.OP_CHECKMULTISIG)
throw new TypeError('Output is invalid');
- if (o.m <= 0 || o.n > 16 || o.m > o.n || o.n !== chunks.length - 3)
+ if (o.m <= 0 || o.n > 20 || o.m > o.n || o.n !== chunks.length - 3)
throw new TypeError('Output is invalid');
- if (!o.pubkeys.every(x => (0, types_1.isPoint)(x)))
+ if (!o.pubkeys.every(x => isPoint(x)))
throw new TypeError('Output is invalid');
if (a.m !== undefined && a.m !== o.m) throw new TypeError('m mismatch');
if (a.n !== undefined && a.n !== o.n) throw new TypeError('n mismatch');
@@ -147,4 +165,3 @@ function p2ms(a, opts) {
}
return Object.assign(o, a);
}
-exports.p2ms = p2ms;
diff --git a/src/payments/p2pk.js b/src/esm/payments/p2pk.js
similarity index 54%
rename from src/payments/p2pk.js
rename to src/esm/payments/p2pk.js
index 284953045..8dba5e1ae 100644
--- a/src/payments/p2pk.js
+++ b/src/esm/payments/p2pk.js
@@ -1,31 +1,43 @@
-'use strict';
-Object.defineProperty(exports, '__esModule', { value: true });
-exports.p2pk = void 0;
-const networks_1 = require('../networks');
-const bscript = require('../script');
-const types_1 = require('../types');
-const lazy = require('./lazy');
+import { bitcoin as BITCOIN_NETWORK } from '../networks.js';
+import * as bscript from '../script.js';
+import { BufferSchema, isPoint } from '../types.js';
+import * as lazy from './lazy.js';
+import * as tools from 'uint8array-tools';
+import * as v from 'valibot';
const OPS = bscript.OPS;
// input: {signature}
// output: {pubKey} OP_CHECKSIG
-function p2pk(a, opts) {
+/**
+ * Creates a pay-to-public-key (P2PK) payment object.
+ *
+ * @param a - The payment object containing the necessary data.
+ * @param opts - Optional payment options.
+ * @returns The P2PK payment object.
+ * @throws {TypeError} If the required data is not provided or if the data is invalid.
+ */
+export function p2pk(a, opts) {
if (!a.input && !a.output && !a.pubkey && !a.input && !a.signature)
throw new TypeError('Not enough data');
opts = Object.assign({ validate: true }, opts || {});
- (0, types_1.typeforce)(
- {
- network: types_1.typeforce.maybe(types_1.typeforce.Object),
- output: types_1.typeforce.maybe(types_1.typeforce.Buffer),
- pubkey: types_1.typeforce.maybe(types_1.isPoint),
- signature: types_1.typeforce.maybe(bscript.isCanonicalScriptSignature),
- input: types_1.typeforce.maybe(types_1.typeforce.Buffer),
- },
+ v.parse(
+ v.partial(
+ v.object({
+ network: v.object({}),
+ output: BufferSchema,
+ pubkey: v.custom(isPoint, 'invalid pubkey'),
+ signature: v.custom(
+ bscript.isCanonicalScriptSignature,
+ 'Expected signature to be of type isCanonicalScriptSignature',
+ ),
+ input: BufferSchema,
+ }),
+ ),
a,
);
const _chunks = lazy.value(() => {
return bscript.decompile(a.input);
});
- const network = a.network || networks_1.bitcoin;
+ const network = a.network || BITCOIN_NETWORK;
const o = { name: 'p2pk', network };
lazy.prop(o, 'output', () => {
if (!a.pubkey) return;
@@ -52,13 +64,12 @@ function p2pk(a, opts) {
if (a.output) {
if (a.output[a.output.length - 1] !== OPS.OP_CHECKSIG)
throw new TypeError('Output is invalid');
- if (!(0, types_1.isPoint)(o.pubkey))
- throw new TypeError('Output pubkey is invalid');
- if (a.pubkey && !a.pubkey.equals(o.pubkey))
+ if (!isPoint(o.pubkey)) throw new TypeError('Output pubkey is invalid');
+ if (a.pubkey && tools.compare(a.pubkey, o.pubkey) !== 0)
throw new TypeError('Pubkey mismatch');
}
if (a.signature) {
- if (a.input && !a.input.equals(o.input))
+ if (a.input && tools.compare(a.input, o.input) !== 0)
throw new TypeError('Signature mismatch');
}
if (a.input) {
@@ -69,4 +80,3 @@ function p2pk(a, opts) {
}
return Object.assign(o, a);
}
-exports.p2pk = p2pk;
diff --git a/src/payments/p2pkh.js b/src/esm/payments/p2pkh.js
similarity index 62%
rename from src/payments/p2pkh.js
rename to src/esm/payments/p2pkh.js
index 8edc8ba9e..41101eb03 100644
--- a/src/payments/p2pkh.js
+++ b/src/esm/payments/p2pkh.js
@@ -1,47 +1,61 @@
-'use strict';
-Object.defineProperty(exports, '__esModule', { value: true });
-exports.p2pkh = void 0;
-const bcrypto = require('../crypto');
-const networks_1 = require('../networks');
-const bscript = require('../script');
-const types_1 = require('../types');
-const lazy = require('./lazy');
-const bs58check = require('bs58check');
+import * as bcrypto from '../crypto.js';
+import { bitcoin as BITCOIN_NETWORK } from '../networks.js';
+import * as bscript from '../script.js';
+import {
+ isPoint,
+ Hash160bitSchema,
+ NBufferSchemaFactory,
+ BufferSchema,
+} from '../types.js';
+import * as lazy from './lazy.js';
+import bs58check from 'bs58check';
+import * as tools from 'uint8array-tools';
+import * as v from 'valibot';
const OPS = bscript.OPS;
// input: {signature} {pubkey}
// output: OP_DUP OP_HASH160 {hash160(pubkey)} OP_EQUALVERIFY OP_CHECKSIG
-function p2pkh(a, opts) {
+/**
+ * Creates a Pay-to-Public-Key-Hash (P2PKH) payment object.
+ *
+ * @param a - The payment object containing the necessary data.
+ * @param opts - Optional payment options.
+ * @returns The P2PKH payment object.
+ * @throws {TypeError} If the required data is not provided or if the data is invalid.
+ */
+export function p2pkh(a, opts) {
if (!a.address && !a.hash && !a.output && !a.pubkey && !a.input)
throw new TypeError('Not enough data');
opts = Object.assign({ validate: true }, opts || {});
- (0, types_1.typeforce)(
- {
- network: types_1.typeforce.maybe(types_1.typeforce.Object),
- address: types_1.typeforce.maybe(types_1.typeforce.String),
- hash: types_1.typeforce.maybe(types_1.typeforce.BufferN(20)),
- output: types_1.typeforce.maybe(types_1.typeforce.BufferN(25)),
- pubkey: types_1.typeforce.maybe(types_1.isPoint),
- signature: types_1.typeforce.maybe(bscript.isCanonicalScriptSignature),
- input: types_1.typeforce.maybe(types_1.typeforce.Buffer),
- },
+ v.parse(
+ v.partial(
+ v.object({
+ network: v.object({}),
+ address: v.string(),
+ hash: Hash160bitSchema,
+ output: NBufferSchemaFactory(25),
+ pubkey: v.custom(isPoint),
+ signature: v.custom(bscript.isCanonicalScriptSignature),
+ input: BufferSchema,
+ }),
+ ),
a,
);
const _address = lazy.value(() => {
const payload = bs58check.decode(a.address);
- const version = payload.readUInt8(0);
+ const version = tools.readUInt8(payload, 0);
const hash = payload.slice(1);
return { version, hash };
});
const _chunks = lazy.value(() => {
return bscript.decompile(a.input);
});
- const network = a.network || networks_1.bitcoin;
+ const network = a.network || BITCOIN_NETWORK;
const o = { name: 'p2pkh', network };
lazy.prop(o, 'address', () => {
if (!o.hash) return;
- const payload = Buffer.allocUnsafe(21);
- payload.writeUInt8(network.pubKeyHash, 0);
- o.hash.copy(payload, 1);
+ const payload = new Uint8Array(21);
+ tools.writeUInt8(payload, 0, network.pubKeyHash);
+ payload.set(o.hash, 1);
return bs58check.encode(payload);
});
lazy.prop(o, 'hash', () => {
@@ -78,7 +92,7 @@ function p2pkh(a, opts) {
});
// extended validation
if (opts.validate) {
- let hash = Buffer.from([]);
+ let hash = Uint8Array.from([]);
if (a.address) {
if (_address().version !== network.pubKeyHash)
throw new TypeError('Invalid version or Network mismatch');
@@ -86,7 +100,7 @@ function p2pkh(a, opts) {
hash = _address().hash;
}
if (a.hash) {
- if (hash.length > 0 && !hash.equals(a.hash))
+ if (hash.length > 0 && tools.compare(hash, a.hash) !== 0)
throw new TypeError('Hash mismatch');
else hash = a.hash;
}
@@ -101,13 +115,13 @@ function p2pkh(a, opts) {
)
throw new TypeError('Output is invalid');
const hash2 = a.output.slice(3, 23);
- if (hash.length > 0 && !hash.equals(hash2))
+ if (hash.length > 0 && tools.compare(hash, hash2) !== 0)
throw new TypeError('Hash mismatch');
else hash = hash2;
}
if (a.pubkey) {
const pkh = bcrypto.hash160(a.pubkey);
- if (hash.length > 0 && !hash.equals(pkh))
+ if (hash.length > 0 && tools.compare(hash, pkh) !== 0)
throw new TypeError('Hash mismatch');
else hash = pkh;
}
@@ -116,17 +130,15 @@ function p2pkh(a, opts) {
if (chunks.length !== 2) throw new TypeError('Input is invalid');
if (!bscript.isCanonicalScriptSignature(chunks[0]))
throw new TypeError('Input has invalid signature');
- if (!(0, types_1.isPoint)(chunks[1]))
- throw new TypeError('Input has invalid pubkey');
- if (a.signature && !a.signature.equals(chunks[0]))
+ if (!isPoint(chunks[1])) throw new TypeError('Input has invalid pubkey');
+ if (a.signature && tools.compare(a.signature, chunks[0]) !== 0)
throw new TypeError('Signature mismatch');
- if (a.pubkey && !a.pubkey.equals(chunks[1]))
+ if (a.pubkey && tools.compare(a.pubkey, chunks[1]) !== 0)
throw new TypeError('Pubkey mismatch');
const pkh = bcrypto.hash160(chunks[1]);
- if (hash.length > 0 && !hash.equals(pkh))
+ if (hash.length > 0 && tools.compare(hash, pkh) !== 0)
throw new TypeError('Hash mismatch');
}
}
return Object.assign(o, a);
}
-exports.p2pkh = p2pkh;
diff --git a/src/payments/p2sh.js b/src/esm/payments/p2sh.js
similarity index 66%
rename from src/payments/p2sh.js
rename to src/esm/payments/p2sh.js
index 8710bf19d..ed2d28c68 100644
--- a/src/payments/p2sh.js
+++ b/src/esm/payments/p2sh.js
@@ -1,55 +1,56 @@
-'use strict';
-Object.defineProperty(exports, '__esModule', { value: true });
-exports.p2sh = void 0;
-const bcrypto = require('../crypto');
-const networks_1 = require('../networks');
-const bscript = require('../script');
-const types_1 = require('../types');
-const lazy = require('./lazy');
-const bs58check = require('bs58check');
+import * as bcrypto from '../crypto.js';
+import { bitcoin as BITCOIN_NETWORK } from '../networks.js';
+import * as bscript from '../script.js';
+import { BufferSchema, NBufferSchemaFactory, stacksEqual } from '../types.js';
+import * as lazy from './lazy.js';
+import bs58check from 'bs58check';
+import * as tools from 'uint8array-tools';
+import * as v from 'valibot';
const OPS = bscript.OPS;
-function stacksEqual(a, b) {
- if (a.length !== b.length) return false;
- return a.every((x, i) => {
- return x.equals(b[i]);
- });
-}
// input: [redeemScriptSig ...] {redeemScript}
// witness: >
// output: OP_HASH160 {hash160(redeemScript)} OP_EQUAL
-function p2sh(a, opts) {
+/**
+ * Creates a Pay-to-Script-Hash (P2SH) payment object.
+ *
+ * @param a - The payment object containing the necessary data.
+ * @param opts - Optional payment options.
+ * @returns The P2SH payment object.
+ * @throws {TypeError} If the required data is not provided or if the data is invalid.
+ */
+export function p2sh(a, opts) {
if (!a.address && !a.hash && !a.output && !a.redeem && !a.input)
throw new TypeError('Not enough data');
opts = Object.assign({ validate: true }, opts || {});
- (0, types_1.typeforce)(
- {
- network: types_1.typeforce.maybe(types_1.typeforce.Object),
- address: types_1.typeforce.maybe(types_1.typeforce.String),
- hash: types_1.typeforce.maybe(types_1.typeforce.BufferN(20)),
- output: types_1.typeforce.maybe(types_1.typeforce.BufferN(23)),
- redeem: types_1.typeforce.maybe({
- network: types_1.typeforce.maybe(types_1.typeforce.Object),
- output: types_1.typeforce.maybe(types_1.typeforce.Buffer),
- input: types_1.typeforce.maybe(types_1.typeforce.Buffer),
- witness: types_1.typeforce.maybe(
- types_1.typeforce.arrayOf(types_1.typeforce.Buffer),
+ v.parse(
+ v.partial(
+ v.object({
+ network: v.object({}),
+ address: v.string(),
+ hash: NBufferSchemaFactory(20),
+ output: NBufferSchemaFactory(23),
+ redeem: v.partial(
+ v.object({
+ network: v.object({}),
+ output: BufferSchema,
+ input: BufferSchema,
+ witness: v.array(BufferSchema),
+ }),
),
+ input: BufferSchema,
+ witness: v.array(BufferSchema),
}),
- input: types_1.typeforce.maybe(types_1.typeforce.Buffer),
- witness: types_1.typeforce.maybe(
- types_1.typeforce.arrayOf(types_1.typeforce.Buffer),
- ),
- },
+ ),
a,
);
let network = a.network;
if (!network) {
- network = (a.redeem && a.redeem.network) || networks_1.bitcoin;
+ network = (a.redeem && a.redeem.network) || BITCOIN_NETWORK;
}
const o = { network };
const _address = lazy.value(() => {
const payload = bs58check.decode(a.address);
- const version = payload.readUInt8(0);
+ const version = tools.readUInt8(payload, 0);
const hash = payload.slice(1);
return { version, hash };
});
@@ -58,9 +59,10 @@ function p2sh(a, opts) {
});
const _redeem = lazy.value(() => {
const chunks = _chunks();
+ const lastChunk = chunks[chunks.length - 1];
return {
network,
- output: chunks[chunks.length - 1],
+ output: lastChunk === OPS.OP_FALSE ? Uint8Array.from([]) : lastChunk,
input: bscript.compile(chunks.slice(0, -1)),
witness: a.witness || [],
};
@@ -68,9 +70,9 @@ function p2sh(a, opts) {
// output dependents
lazy.prop(o, 'address', () => {
if (!o.hash) return;
- const payload = Buffer.allocUnsafe(21);
- payload.writeUInt8(o.network.scriptHash, 0);
- o.hash.copy(payload, 1);
+ const payload = new Uint8Array(21);
+ tools.writeUInt8(payload, 0, o.network.scriptHash);
+ payload.set(o.hash, 1);
return bs58check.encode(payload);
});
lazy.prop(o, 'hash', () => {
@@ -105,7 +107,7 @@ function p2sh(a, opts) {
return nameParts.join('-');
});
if (opts.validate) {
- let hash = Buffer.from([]);
+ let hash = Uint8Array.from([]);
if (a.address) {
if (_address().version !== network.scriptHash)
throw new TypeError('Invalid version or Network mismatch');
@@ -113,7 +115,7 @@ function p2sh(a, opts) {
hash = _address().hash;
}
if (a.hash) {
- if (hash.length > 0 && !hash.equals(a.hash))
+ if (hash.length > 0 && tools.compare(hash, a.hash) !== 0)
throw new TypeError('Hash mismatch');
else hash = a.hash;
}
@@ -126,7 +128,7 @@ function p2sh(a, opts) {
)
throw new TypeError('Output is invalid');
const hash2 = a.output.slice(2, 22);
- if (hash.length > 0 && !hash.equals(hash2))
+ if (hash.length > 0 && tools.compare(hash, hash2) !== 0)
throw new TypeError('Hash mismatch');
else hash = hash2;
}
@@ -137,9 +139,17 @@ function p2sh(a, opts) {
const decompile = bscript.decompile(redeem.output);
if (!decompile || decompile.length < 1)
throw new TypeError('Redeem.output too short');
+ if (redeem.output.byteLength > 520)
+ throw new TypeError(
+ 'Redeem.output unspendable if larger than 520 bytes',
+ );
+ if (bscript.countNonPushOnlyOPs(decompile) > 201)
+ throw new TypeError(
+ 'Redeem.output unspendable with more than 201 non-push ops',
+ );
// match hash against other sources
const hash2 = bcrypto.hash160(redeem.output);
- if (hash.length > 0 && !hash.equals(hash2))
+ if (hash.length > 0 && tools.compare(hash, hash2) !== 0)
throw new TypeError('Hash mismatch');
else hash = hash2;
}
@@ -159,7 +169,7 @@ function p2sh(a, opts) {
if (a.input) {
const chunks = _chunks();
if (!chunks || chunks.length < 1) throw new TypeError('Input too short');
- if (!Buffer.isBuffer(_redeem().output))
+ if (!(_redeem().output instanceof Uint8Array))
throw new TypeError('Input is invalid');
checkRedeem(_redeem());
}
@@ -168,9 +178,12 @@ function p2sh(a, opts) {
throw new TypeError('Network mismatch');
if (a.input) {
const redeem = _redeem();
- if (a.redeem.output && !a.redeem.output.equals(redeem.output))
+ if (
+ a.redeem.output &&
+ tools.compare(a.redeem.output, redeem.output) !== 0
+ )
throw new TypeError('Redeem.output mismatch');
- if (a.redeem.input && !a.redeem.input.equals(redeem.input))
+ if (a.redeem.input && tools.compare(a.redeem.input, redeem.input) !== 0)
throw new TypeError('Redeem.input mismatch');
}
checkRedeem(a.redeem);
@@ -186,4 +199,3 @@ function p2sh(a, opts) {
}
return Object.assign(o, a);
}
-exports.p2sh = p2sh;
diff --git a/src/esm/payments/p2tr.js b/src/esm/payments/p2tr.js
new file mode 100644
index 000000000..d12fabdbc
--- /dev/null
+++ b/src/esm/payments/p2tr.js
@@ -0,0 +1,301 @@
+import { bitcoin as BITCOIN_NETWORK } from '../networks.js';
+import * as bscript from '../script.js';
+import {
+ isTaptree,
+ TAPLEAF_VERSION_MASK,
+ stacksEqual,
+ NBufferSchemaFactory,
+ BufferSchema,
+} from '../types.js';
+import { getEccLib } from '../ecc_lib.js';
+import {
+ toHashTree,
+ rootHashFromPath,
+ findScriptPath,
+ tapleafHash,
+ tweakKey,
+ LEAF_VERSION_TAPSCRIPT,
+} from './bip341.js';
+import * as lazy from './lazy.js';
+import { bech32m } from 'bech32';
+import { fromBech32 } from '../address.js';
+import * as tools from 'uint8array-tools';
+import * as v from 'valibot';
+const OPS = bscript.OPS;
+const TAPROOT_WITNESS_VERSION = 0x01;
+const ANNEX_PREFIX = 0x50;
+/**
+ * Creates a Pay-to-Taproot (P2TR) payment object.
+ *
+ * @param a - The payment object containing the necessary data for P2TR.
+ * @param opts - Optional payment options.
+ * @returns The P2TR payment object.
+ * @throws {TypeError} If the provided data is invalid or insufficient.
+ */
+export function p2tr(a, opts) {
+ if (
+ !a.address &&
+ !a.output &&
+ !a.pubkey &&
+ !a.internalPubkey &&
+ !(a.witness && a.witness.length > 1)
+ )
+ throw new TypeError('Not enough data');
+ opts = Object.assign({ validate: true }, opts || {});
+ v.parse(
+ v.partial(
+ v.object({
+ address: v.string(),
+ input: NBufferSchemaFactory(0),
+ network: v.object({}),
+ output: NBufferSchemaFactory(34),
+ internalPubkey: NBufferSchemaFactory(32),
+ hash: NBufferSchemaFactory(32), // merkle root hash, the tweak
+ pubkey: NBufferSchemaFactory(32), // tweaked with `hash` from `internalPubkey`
+ signature: v.union([
+ NBufferSchemaFactory(64),
+ NBufferSchemaFactory(65),
+ ]),
+ witness: v.array(BufferSchema),
+ scriptTree: v.custom(isTaptree, 'Taptree is not of type isTaptree'),
+ redeem: v.partial(
+ v.object({
+ output: BufferSchema, // tapleaf script
+ redeemVersion: v.number(), // tapleaf version
+ witness: v.array(BufferSchema),
+ }),
+ ),
+ redeemVersion: v.number(),
+ }),
+ ),
+ a,
+ );
+ const _address = lazy.value(() => {
+ return fromBech32(a.address);
+ });
+ // remove annex if present, ignored by taproot
+ const _witness = lazy.value(() => {
+ if (!a.witness || !a.witness.length) return;
+ if (
+ a.witness.length >= 2 &&
+ a.witness[a.witness.length - 1][0] === ANNEX_PREFIX
+ ) {
+ return a.witness.slice(0, -1);
+ }
+ return a.witness.slice();
+ });
+ const _hashTree = lazy.value(() => {
+ if (a.scriptTree) return toHashTree(a.scriptTree);
+ if (a.hash) return { hash: a.hash };
+ return;
+ });
+ const network = a.network || BITCOIN_NETWORK;
+ const o = { name: 'p2tr', network };
+ lazy.prop(o, 'address', () => {
+ if (!o.pubkey) return;
+ const words = bech32m.toWords(o.pubkey);
+ words.unshift(TAPROOT_WITNESS_VERSION);
+ return bech32m.encode(network.bech32, words);
+ });
+ lazy.prop(o, 'hash', () => {
+ const hashTree = _hashTree();
+ if (hashTree) return hashTree.hash;
+ const w = _witness();
+ if (w && w.length > 1) {
+ const controlBlock = w[w.length - 1];
+ const leafVersion = controlBlock[0] & TAPLEAF_VERSION_MASK;
+ const script = w[w.length - 2];
+ const leafHash = tapleafHash({ output: script, version: leafVersion });
+ return rootHashFromPath(controlBlock, leafHash);
+ }
+ return null;
+ });
+ lazy.prop(o, 'output', () => {
+ if (!o.pubkey) return;
+ return bscript.compile([OPS.OP_1, o.pubkey]);
+ });
+ lazy.prop(o, 'redeemVersion', () => {
+ if (a.redeemVersion) return a.redeemVersion;
+ if (
+ a.redeem &&
+ a.redeem.redeemVersion !== undefined &&
+ a.redeem.redeemVersion !== null
+ ) {
+ return a.redeem.redeemVersion;
+ }
+ return LEAF_VERSION_TAPSCRIPT;
+ });
+ lazy.prop(o, 'redeem', () => {
+ const witness = _witness(); // witness without annex
+ if (!witness || witness.length < 2) return;
+ return {
+ output: witness[witness.length - 2],
+ witness: witness.slice(0, -2),
+ redeemVersion: witness[witness.length - 1][0] & TAPLEAF_VERSION_MASK,
+ };
+ });
+ lazy.prop(o, 'pubkey', () => {
+ if (a.pubkey) return a.pubkey;
+ if (a.output) return a.output.slice(2);
+ if (a.address) return _address().data;
+ if (o.internalPubkey) {
+ const tweakedKey = tweakKey(o.internalPubkey, o.hash);
+ if (tweakedKey) return tweakedKey.x;
+ }
+ });
+ lazy.prop(o, 'internalPubkey', () => {
+ if (a.internalPubkey) return a.internalPubkey;
+ const witness = _witness();
+ if (witness && witness.length > 1)
+ return witness[witness.length - 1].slice(1, 33);
+ });
+ lazy.prop(o, 'signature', () => {
+ if (a.signature) return a.signature;
+ const witness = _witness(); // witness without annex
+ if (!witness || witness.length !== 1) return;
+ return witness[0];
+ });
+ lazy.prop(o, 'witness', () => {
+ if (a.witness) return a.witness;
+ const hashTree = _hashTree();
+ if (hashTree && a.redeem && a.redeem.output && a.internalPubkey) {
+ const leafHash = tapleafHash({
+ output: a.redeem.output,
+ version: o.redeemVersion,
+ });
+ const path = findScriptPath(hashTree, leafHash);
+ if (!path) return;
+ const outputKey = tweakKey(a.internalPubkey, hashTree.hash);
+ if (!outputKey) return;
+ const controlBock = tools.concat(
+ [
+ Uint8Array.from([o.redeemVersion | outputKey.parity]),
+ a.internalPubkey,
+ ].concat(path),
+ );
+ return [a.redeem.output, controlBock];
+ }
+ if (a.signature) return [a.signature];
+ });
+ // extended validation
+ if (opts.validate) {
+ let pubkey = Uint8Array.from([]);
+ if (a.address) {
+ if (network && network.bech32 !== _address().prefix)
+ throw new TypeError('Invalid prefix or Network mismatch');
+ if (_address().version !== TAPROOT_WITNESS_VERSION)
+ throw new TypeError('Invalid address version');
+ if (_address().data.length !== 32)
+ throw new TypeError('Invalid address data');
+ pubkey = _address().data;
+ }
+ if (a.pubkey) {
+ if (pubkey.length > 0 && tools.compare(pubkey, a.pubkey) !== 0)
+ throw new TypeError('Pubkey mismatch');
+ else pubkey = a.pubkey;
+ }
+ if (a.output) {
+ if (
+ a.output.length !== 34 ||
+ a.output[0] !== OPS.OP_1 ||
+ a.output[1] !== 0x20
+ )
+ throw new TypeError('Output is invalid');
+ if (pubkey.length > 0 && tools.compare(pubkey, a.output.slice(2)) !== 0)
+ throw new TypeError('Pubkey mismatch');
+ else pubkey = a.output.slice(2);
+ }
+ if (a.internalPubkey) {
+ const tweakedKey = tweakKey(a.internalPubkey, o.hash);
+ if (pubkey.length > 0 && tools.compare(pubkey, tweakedKey.x) !== 0)
+ throw new TypeError('Pubkey mismatch');
+ else pubkey = tweakedKey.x;
+ }
+ if (pubkey && pubkey.length) {
+ if (!getEccLib().isXOnlyPoint(pubkey))
+ throw new TypeError('Invalid pubkey for p2tr');
+ }
+ const hashTree = _hashTree();
+ if (a.hash && hashTree) {
+ if (tools.compare(a.hash, hashTree.hash) !== 0)
+ throw new TypeError('Hash mismatch');
+ }
+ if (a.redeem && a.redeem.output && hashTree) {
+ const leafHash = tapleafHash({
+ output: a.redeem.output,
+ version: o.redeemVersion,
+ });
+ if (!findScriptPath(hashTree, leafHash))
+ throw new TypeError('Redeem script not in tree');
+ }
+ const witness = _witness();
+ // compare the provided redeem data with the one computed from witness
+ if (a.redeem && o.redeem) {
+ if (a.redeem.redeemVersion) {
+ if (a.redeem.redeemVersion !== o.redeem.redeemVersion)
+ throw new TypeError('Redeem.redeemVersion and witness mismatch');
+ }
+ if (a.redeem.output) {
+ if (bscript.decompile(a.redeem.output).length === 0)
+ throw new TypeError('Redeem.output is invalid');
+ // output redeem is constructed from the witness
+ if (
+ o.redeem.output &&
+ tools.compare(a.redeem.output, o.redeem.output) !== 0
+ )
+ throw new TypeError('Redeem.output and witness mismatch');
+ }
+ if (a.redeem.witness) {
+ if (
+ o.redeem.witness &&
+ !stacksEqual(a.redeem.witness, o.redeem.witness)
+ )
+ throw new TypeError('Redeem.witness and witness mismatch');
+ }
+ }
+ if (witness && witness.length) {
+ if (witness.length === 1) {
+ // key spending
+ if (a.signature && tools.compare(a.signature, witness[0]) !== 0)
+ throw new TypeError('Signature mismatch');
+ } else {
+ // script path spending
+ const controlBlock = witness[witness.length - 1];
+ if (controlBlock.length < 33)
+ throw new TypeError(
+ `The control-block length is too small. Got ${controlBlock.length}, expected min 33.`,
+ );
+ if ((controlBlock.length - 33) % 32 !== 0)
+ throw new TypeError(
+ `The control-block length of ${controlBlock.length} is incorrect!`,
+ );
+ const m = (controlBlock.length - 33) / 32;
+ if (m > 128)
+ throw new TypeError(
+ `The script path is too long. Got ${m}, expected max 128.`,
+ );
+ const internalPubkey = controlBlock.slice(1, 33);
+ if (
+ a.internalPubkey &&
+ tools.compare(a.internalPubkey, internalPubkey) !== 0
+ )
+ throw new TypeError('Internal pubkey mismatch');
+ if (!getEccLib().isXOnlyPoint(internalPubkey))
+ throw new TypeError('Invalid internalPubkey for p2tr witness');
+ const leafVersion = controlBlock[0] & TAPLEAF_VERSION_MASK;
+ const script = witness[witness.length - 2];
+ const leafHash = tapleafHash({ output: script, version: leafVersion });
+ const hash = rootHashFromPath(controlBlock, leafHash);
+ const outputKey = tweakKey(internalPubkey, hash);
+ if (!outputKey)
+ // todo: needs test data
+ throw new TypeError('Invalid outputKey for p2tr witness');
+ if (pubkey.length && tools.compare(pubkey, outputKey.x) !== 0)
+ throw new TypeError('Pubkey mismatch for p2tr witness');
+ if (outputKey.parity !== (controlBlock[0] & 1))
+ throw new Error('Incorrect parity');
+ }
+ }
+ }
+ return Object.assign(o, a);
+}
diff --git a/src/payments/p2wpkh.js b/src/esm/payments/p2wpkh.js
similarity index 58%
rename from src/payments/p2wpkh.js
rename to src/esm/payments/p2wpkh.js
index 168e08f19..7b550736c 100644
--- a/src/payments/p2wpkh.js
+++ b/src/esm/payments/p2wpkh.js
@@ -1,53 +1,60 @@
-'use strict';
-Object.defineProperty(exports, '__esModule', { value: true });
-exports.p2wpkh = void 0;
-const bcrypto = require('../crypto');
-const networks_1 = require('../networks');
-const bscript = require('../script');
-const types_1 = require('../types');
-const lazy = require('./lazy');
-const bech32_1 = require('bech32');
+import * as bcrypto from '../crypto.js';
+import { bitcoin as BITCOIN_NETWORK } from '../networks.js';
+import * as bscript from '../script.js';
+import { BufferSchema, isPoint, NBufferSchemaFactory } from '../types.js';
+import * as lazy from './lazy.js';
+import { bech32 } from 'bech32';
+import * as tools from 'uint8array-tools';
+import * as v from 'valibot';
const OPS = bscript.OPS;
-const EMPTY_BUFFER = Buffer.alloc(0);
+const EMPTY_BUFFER = new Uint8Array(0);
// witness: {signature} {pubKey}
// input: <>
// output: OP_0 {pubKeyHash}
-function p2wpkh(a, opts) {
+/**
+ * Creates a pay-to-witness-public-key-hash (p2wpkh) payment object.
+ *
+ * @param a - The payment object containing the necessary data.
+ * @param opts - Optional payment options.
+ * @returns The p2wpkh payment object.
+ * @throws {TypeError} If the required data is missing or invalid.
+ */
+export function p2wpkh(a, opts) {
if (!a.address && !a.hash && !a.output && !a.pubkey && !a.witness)
throw new TypeError('Not enough data');
opts = Object.assign({ validate: true }, opts || {});
- (0, types_1.typeforce)(
- {
- address: types_1.typeforce.maybe(types_1.typeforce.String),
- hash: types_1.typeforce.maybe(types_1.typeforce.BufferN(20)),
- input: types_1.typeforce.maybe(types_1.typeforce.BufferN(0)),
- network: types_1.typeforce.maybe(types_1.typeforce.Object),
- output: types_1.typeforce.maybe(types_1.typeforce.BufferN(22)),
- pubkey: types_1.typeforce.maybe(types_1.isPoint),
- signature: types_1.typeforce.maybe(bscript.isCanonicalScriptSignature),
- witness: types_1.typeforce.maybe(
- types_1.typeforce.arrayOf(types_1.typeforce.Buffer),
- ),
- },
+ v.parse(
+ v.partial(
+ v.object({
+ address: v.string(),
+ hash: NBufferSchemaFactory(20),
+ input: NBufferSchemaFactory(0),
+ network: v.object({}),
+ output: NBufferSchemaFactory(22),
+ pubkey: v.custom(isPoint, 'Not a valid pubkey'),
+ signature: v.custom(bscript.isCanonicalScriptSignature),
+ witness: v.array(BufferSchema),
+ }),
+ ),
a,
);
const _address = lazy.value(() => {
- const result = bech32_1.bech32.decode(a.address);
+ const result = bech32.decode(a.address);
const version = result.words.shift();
- const data = bech32_1.bech32.fromWords(result.words);
+ const data = bech32.fromWords(result.words);
return {
version,
prefix: result.prefix,
- data: Buffer.from(data),
+ data: Uint8Array.from(data),
};
});
- const network = a.network || networks_1.bitcoin;
+ const network = a.network || BITCOIN_NETWORK;
const o = { name: 'p2wpkh', network };
lazy.prop(o, 'address', () => {
if (!o.hash) return;
- const words = bech32_1.bech32.toWords(o.hash);
+ const words = bech32.toWords(o.hash);
words.unshift(0x00);
- return bech32_1.bech32.encode(network.bech32, words);
+ return bech32.encode(network.bech32, words);
});
lazy.prop(o, 'hash', () => {
if (a.output) return a.output.slice(2, 22);
@@ -78,7 +85,7 @@ function p2wpkh(a, opts) {
});
// extended validation
if (opts.validate) {
- let hash = Buffer.from([]);
+ let hash = Uint8Array.from([]);
if (a.address) {
if (network && network.bech32 !== _address().prefix)
throw new TypeError('Invalid prefix or Network mismatch');
@@ -89,7 +96,7 @@ function p2wpkh(a, opts) {
hash = _address().data;
}
if (a.hash) {
- if (hash.length > 0 && !hash.equals(a.hash))
+ if (hash.length > 0 && tools.compare(hash, a.hash) !== 0)
throw new TypeError('Hash mismatch');
else hash = a.hash;
}
@@ -100,33 +107,33 @@ function p2wpkh(a, opts) {
a.output[1] !== 0x14
)
throw new TypeError('Output is invalid');
- if (hash.length > 0 && !hash.equals(a.output.slice(2)))
+ if (hash.length > 0 && tools.compare(hash, a.output.slice(2)) !== 0)
throw new TypeError('Hash mismatch');
else hash = a.output.slice(2);
}
if (a.pubkey) {
const pkh = bcrypto.hash160(a.pubkey);
- if (hash.length > 0 && !hash.equals(pkh))
+ if (hash.length > 0 && tools.compare(hash, pkh) !== 0)
throw new TypeError('Hash mismatch');
else hash = pkh;
- if (!(0, types_1.isPoint)(a.pubkey) || a.pubkey.length !== 33)
+ if (!isPoint(a.pubkey) || a.pubkey.length !== 33)
throw new TypeError('Invalid pubkey for p2wpkh');
}
if (a.witness) {
if (a.witness.length !== 2) throw new TypeError('Witness is invalid');
if (!bscript.isCanonicalScriptSignature(a.witness[0]))
throw new TypeError('Witness has invalid signature');
- if (!(0, types_1.isPoint)(a.witness[1]) || a.witness[1].length !== 33)
+ if (!isPoint(a.witness[1]) || a.witness[1].length !== 33)
throw new TypeError('Witness has invalid pubkey');
- if (a.signature && !a.signature.equals(a.witness[0]))
+ if (a.signature && tools.compare(a.signature, a.witness[0]) !== 0)
throw new TypeError('Signature mismatch');
- if (a.pubkey && !a.pubkey.equals(a.witness[1]))
+ // if (a.pubkey && !a.pubkey.equals(a.witness[1]))
+ if (a.pubkey && tools.compare(a.pubkey, a.witness[1]) !== 0)
throw new TypeError('Pubkey mismatch');
const pkh = bcrypto.hash160(a.witness[1]);
- if (hash.length > 0 && !hash.equals(pkh))
+ if (hash.length > 0 && tools.compare(hash, pkh) !== 0)
throw new TypeError('Hash mismatch');
}
}
return Object.assign(o, a);
}
-exports.p2wpkh = p2wpkh;
diff --git a/src/payments/p2wsh.js b/src/esm/payments/p2wsh.js
similarity index 65%
rename from src/payments/p2wsh.js
rename to src/esm/payments/p2wsh.js
index 66ee1da02..0ca099bb6 100644
--- a/src/payments/p2wsh.js
+++ b/src/esm/payments/p2wsh.js
@@ -1,26 +1,26 @@
-'use strict';
-Object.defineProperty(exports, '__esModule', { value: true });
-exports.p2wsh = void 0;
-const bcrypto = require('../crypto');
-const networks_1 = require('../networks');
-const bscript = require('../script');
-const types_1 = require('../types');
-const lazy = require('./lazy');
-const bech32_1 = require('bech32');
+import { sha256 } from '@noble/hashes/sha256';
+import { bitcoin as BITCOIN_NETWORK } from '../networks.js';
+import * as bscript from '../script.js';
+import {
+ Buffer256bitSchema,
+ BufferSchema,
+ isPoint,
+ NBufferSchemaFactory,
+ stacksEqual,
+ NullablePartial,
+} from '../types.js';
+import * as lazy from './lazy.js';
+import { bech32 } from 'bech32';
+import * as tools from 'uint8array-tools';
+import * as v from 'valibot';
const OPS = bscript.OPS;
-const EMPTY_BUFFER = Buffer.alloc(0);
-function stacksEqual(a, b) {
- if (a.length !== b.length) return false;
- return a.every((x, i) => {
- return x.equals(b[i]);
- });
-}
+const EMPTY_BUFFER = new Uint8Array(0);
function chunkHasUncompressedPubkey(chunk) {
if (
- Buffer.isBuffer(chunk) &&
+ chunk instanceof Uint8Array &&
chunk.length === 65 &&
chunk[0] === 0x04 &&
- (0, types_1.isPoint)(chunk)
+ isPoint(chunk)
) {
return true;
} else {
@@ -30,39 +30,43 @@ function chunkHasUncompressedPubkey(chunk) {
// input: <>
// witness: [redeemScriptSig ...] {redeemScript}
// output: OP_0 {sha256(redeemScript)}
-function p2wsh(a, opts) {
+/**
+ * Creates a Pay-to-Witness-Script-Hash (P2WSH) payment object.
+ *
+ * @param a - The payment object containing the necessary data.
+ * @param opts - Optional payment options.
+ * @returns The P2WSH payment object.
+ * @throws {TypeError} If the required data is missing or invalid.
+ */
+export function p2wsh(a, opts) {
if (!a.address && !a.hash && !a.output && !a.redeem && !a.witness)
throw new TypeError('Not enough data');
opts = Object.assign({ validate: true }, opts || {});
- (0, types_1.typeforce)(
- {
- network: types_1.typeforce.maybe(types_1.typeforce.Object),
- address: types_1.typeforce.maybe(types_1.typeforce.String),
- hash: types_1.typeforce.maybe(types_1.typeforce.BufferN(32)),
- output: types_1.typeforce.maybe(types_1.typeforce.BufferN(34)),
- redeem: types_1.typeforce.maybe({
- input: types_1.typeforce.maybe(types_1.typeforce.Buffer),
- network: types_1.typeforce.maybe(types_1.typeforce.Object),
- output: types_1.typeforce.maybe(types_1.typeforce.Buffer),
- witness: types_1.typeforce.maybe(
- types_1.typeforce.arrayOf(types_1.typeforce.Buffer),
- ),
+ v.parse(
+ NullablePartial({
+ network: v.object({}),
+ address: v.string(),
+ hash: Buffer256bitSchema,
+ output: NBufferSchemaFactory(34),
+ redeem: NullablePartial({
+ input: BufferSchema,
+ network: v.object({}),
+ output: BufferSchema,
+ witness: v.array(BufferSchema),
}),
- input: types_1.typeforce.maybe(types_1.typeforce.BufferN(0)),
- witness: types_1.typeforce.maybe(
- types_1.typeforce.arrayOf(types_1.typeforce.Buffer),
- ),
- },
+ input: NBufferSchemaFactory(0),
+ witness: v.array(BufferSchema),
+ }),
a,
);
const _address = lazy.value(() => {
- const result = bech32_1.bech32.decode(a.address);
+ const result = bech32.decode(a.address);
const version = result.words.shift();
- const data = bech32_1.bech32.fromWords(result.words);
+ const data = bech32.fromWords(result.words);
return {
version,
prefix: result.prefix,
- data: Buffer.from(data),
+ data: Uint8Array.from(data),
};
});
const _rchunks = lazy.value(() => {
@@ -70,19 +74,19 @@ function p2wsh(a, opts) {
});
let network = a.network;
if (!network) {
- network = (a.redeem && a.redeem.network) || networks_1.bitcoin;
+ network = (a.redeem && a.redeem.network) || BITCOIN_NETWORK;
}
const o = { network };
lazy.prop(o, 'address', () => {
if (!o.hash) return;
- const words = bech32_1.bech32.toWords(o.hash);
+ const words = bech32.toWords(o.hash);
words.unshift(0x00);
- return bech32_1.bech32.encode(network.bech32, words);
+ return bech32.encode(network.bech32, words);
});
lazy.prop(o, 'hash', () => {
if (a.output) return a.output.slice(2);
if (a.address) return _address().data;
- if (o.redeem && o.redeem.output) return bcrypto.sha256(o.redeem.output);
+ if (o.redeem && o.redeem.output) return sha256(o.redeem.output);
});
lazy.prop(o, 'output', () => {
if (!o.hash) return;
@@ -128,7 +132,7 @@ function p2wsh(a, opts) {
});
// extended validation
if (opts.validate) {
- let hash = Buffer.from([]);
+ let hash = Uint8Array.from([]);
if (a.address) {
if (_address().prefix !== network.bech32)
throw new TypeError('Invalid prefix or Network mismatch');
@@ -139,7 +143,7 @@ function p2wsh(a, opts) {
hash = _address().data;
}
if (a.hash) {
- if (hash.length > 0 && !hash.equals(a.hash))
+ if (hash.length > 0 && tools.compare(hash, a.hash) !== 0)
throw new TypeError('Hash mismatch');
else hash = a.hash;
}
@@ -151,7 +155,7 @@ function p2wsh(a, opts) {
)
throw new TypeError('Output is invalid');
const hash2 = a.output.slice(2);
- if (hash.length > 0 && !hash.equals(hash2))
+ if (hash.length > 0 && tools.compare(hash, hash2) !== 0)
throw new TypeError('Hash mismatch');
else hash = hash2;
}
@@ -166,13 +170,22 @@ function p2wsh(a, opts) {
a.redeem.witness.length > 0
)
throw new TypeError('Ambiguous witness source');
- // is the redeem output non-empty?
+ // is the redeem output non-empty/valid?
if (a.redeem.output) {
- if (bscript.decompile(a.redeem.output).length === 0)
+ const decompile = bscript.decompile(a.redeem.output);
+ if (!decompile || decompile.length < 1)
throw new TypeError('Redeem.output is invalid');
+ if (a.redeem.output.byteLength > 3600)
+ throw new TypeError(
+ 'Redeem.output unspendable if larger than 3600 bytes',
+ );
+ if (bscript.countNonPushOnlyOPs(decompile) > 201)
+ throw new TypeError(
+ 'Redeem.output unspendable with more than 201 non-push ops',
+ );
// match hash against other sources
- const hash2 = bcrypto.sha256(a.redeem.output);
- if (hash.length > 0 && !hash.equals(hash2))
+ const hash2 = sha256(a.redeem.output);
+ if (hash.length > 0 && tools.compare(hash, hash2) !== 0)
throw new TypeError('Hash mismatch');
else hash = hash2;
}
@@ -198,7 +211,11 @@ function p2wsh(a, opts) {
}
if (a.witness && a.witness.length > 0) {
const wScript = a.witness[a.witness.length - 1];
- if (a.redeem && a.redeem.output && !a.redeem.output.equals(wScript))
+ if (
+ a.redeem &&
+ a.redeem.output &&
+ tools.compare(a.redeem.output, wScript) !== 0
+ )
throw new TypeError('Witness and redeem.output mismatch');
if (
a.witness.some(chunkHasUncompressedPubkey) ||
@@ -209,4 +226,3 @@ function p2wsh(a, opts) {
}
return Object.assign(o, a);
}
-exports.p2wsh = p2wsh;
diff --git a/src/psbt.js b/src/esm/psbt.js
similarity index 67%
rename from src/psbt.js
rename to src/esm/psbt.js
index 616219580..15c5e016d 100644
--- a/src/psbt.js
+++ b/src/esm/psbt.js
@@ -1,16 +1,36 @@
-'use strict';
-Object.defineProperty(exports, '__esModule', { value: true });
-exports.Psbt = void 0;
-const bip174_1 = require('bip174');
-const varuint = require('bip174/src/lib/converter/varint');
-const utils_1 = require('bip174/src/lib/utils');
-const address_1 = require('./address');
-const bufferutils_1 = require('./bufferutils');
-const crypto_1 = require('./crypto');
-const networks_1 = require('./networks');
-const payments = require('./payments');
-const bscript = require('./script');
-const transaction_1 = require('./transaction');
+import { Psbt as PsbtBase } from 'bip174';
+import * as varuint from 'varuint-bitcoin';
+import { checkForInput, checkForOutput } from 'bip174';
+import { fromOutputScript, toOutputScript } from './address.js';
+import { cloneBuffer, reverseBuffer } from './bufferutils.js';
+import { bitcoin as btcNetwork } from './networks.js';
+import * as payments from './payments/index.js';
+import { tapleafHash } from './payments/bip341.js';
+import * as bscript from './script.js';
+import { Transaction } from './transaction.js';
+import {
+ toXOnly,
+ tapScriptFinalizer,
+ serializeTaprootSignature,
+ isTaprootInput,
+ checkTaprootInputFields,
+ checkTaprootOutputFields,
+ checkTaprootInputForSigs,
+} from './psbt/bip371.js';
+import {
+ witnessStackToScriptWitness,
+ checkInputForSig,
+ pubkeyInScript,
+ isP2MS,
+ isP2PK,
+ isP2PKH,
+ isP2WPKH,
+ isP2WSHScript,
+ isP2SHScript,
+ isP2TR,
+} from './psbt/psbtutils.js';
+import * as tools from 'uint8array-tools';
+export { toXOnly };
/**
* These are the default arguments for a Psbt instance.
*/
@@ -19,7 +39,7 @@ const DEFAULT_OPTS = {
* A bitcoinjs Network object. This is only used if you pass an `address`
* parameter to addOutput. Otherwise it is not needed and can be left default.
*/
- network: networks_1.bitcoin,
+ network: btcNetwork,
/**
* When extractTransaction is called, the fee rate is checked.
* THIS IS NOT TO BE RELIED ON.
@@ -32,6 +52,7 @@ const DEFAULT_OPTS = {
* There are 6 roles that this class fulfills. (Explained in BIP174)
*
* Creator: This can be done with `new Psbt()`
+ *
* Updater: This can be done with `psbt.addInput(input)`, `psbt.addInputs(inputs)`,
* `psbt.addOutput(output)`, `psbt.addOutputs(outputs)` when you are looking to
* add new inputs and outputs to the PSBT, and `psbt.updateGlobal(itemObject)`,
@@ -42,25 +63,46 @@ const DEFAULT_OPTS = {
* data for updateOutput.
* For a list of what attributes should be what types. Check the bip174 library.
* Also, check the integration tests for some examples of usage.
+ *
* Signer: There are a few methods. signAllInputs and signAllInputsAsync, which will search all input
* information for your pubkey or pubkeyhash, and only sign inputs where it finds
* your info. Or you can explicitly sign a specific input with signInput and
* signInputAsync. For the async methods you can create a SignerAsync object
* and use something like a hardware wallet to sign with. (You must implement this)
+ *
* Combiner: psbts can be combined easily with `psbt.combine(psbt2, psbt3, psbt4 ...)`
* the psbt calling combine will always have precedence when a conflict occurs.
* Combine checks if the internal bitcoin transaction is the same, so be sure that
* all sequences, version, locktime, etc. are the same before combining.
+ *
* Input Finalizer: This role is fairly important. Not only does it need to construct
* the input scriptSigs and witnesses, but it SHOULD verify the signatures etc.
* Before running `psbt.finalizeAllInputs()` please run `psbt.validateSignaturesOfAllInputs()`
* Running any finalize method will delete any data in the input(s) that are no longer
* needed due to the finalized scripts containing the information.
+ *
* Transaction Extractor: This role will perform some checks before returning a
* Transaction object. Such as fee rate not being larger than maximumFeeRate etc.
*/
-class Psbt {
- constructor(opts = {}, data = new bip174_1.Psbt(new PsbtTransaction())) {
+export class Psbt {
+ data;
+ static fromBase64(data, opts = {}) {
+ const buffer = tools.fromBase64(data);
+ return this.fromBuffer(buffer, opts);
+ }
+ static fromHex(data, opts = {}) {
+ const buffer = tools.fromHex(data);
+ return this.fromBuffer(buffer, opts);
+ }
+ static fromBuffer(buffer, opts = {}) {
+ const psbtBase = PsbtBase.fromBuffer(buffer, transactionFromBuffer);
+ const psbt = new Psbt(opts, psbtBase);
+ checkTxForDupeIns(psbt.__CACHE.__TX, psbt.__CACHE);
+ return psbt;
+ }
+ __CACHE;
+ opts;
+ constructor(opts = {}, data = new PsbtBase(new PsbtTransaction())) {
this.data = data;
// set defaults
this.opts = Object.assign({}, DEFAULT_OPTS, opts);
@@ -69,7 +111,7 @@ class Psbt {
__NON_WITNESS_UTXO_BUF_CACHE: [],
__TX_IN_CACHE: {},
__TX: this.data.globalMap.unsignedTx.tx,
- // Psbt's predecesor (TransactionBuilder - now removed) behavior
+ // Psbt's predecessor (TransactionBuilder - now removed) behavior
// was to not confirm input values before signing.
// Even though we highly encourage people to get
// the full parent transaction to verify values, the ability to
@@ -89,20 +131,6 @@ class Psbt {
dpew(this, '__CACHE', false, true);
dpew(this, 'opts', false, true);
}
- static fromBase64(data, opts = {}) {
- const buffer = Buffer.from(data, 'base64');
- return this.fromBuffer(buffer, opts);
- }
- static fromHex(data, opts = {}) {
- const buffer = Buffer.from(data, 'hex');
- return this.fromBuffer(buffer, opts);
- }
- static fromBuffer(buffer, opts = {}) {
- const psbtBase = bip174_1.Psbt.fromBuffer(buffer, transactionFromBuffer);
- const psbt = new Psbt(opts, psbtBase);
- checkTxForDupeIns(psbt.__CACHE.__TX, psbt.__CACHE);
- return psbt;
- }
get inputCount() {
return this.data.inputs.length;
}
@@ -120,7 +148,7 @@ class Psbt {
}
get txInputs() {
return this.__CACHE.__TX.ins.map(input => ({
- hash: (0, bufferutils_1.cloneBuffer)(input.hash),
+ hash: cloneBuffer(input.hash),
index: input.index,
sequence: input.sequence,
}));
@@ -129,13 +157,10 @@ class Psbt {
return this.__CACHE.__TX.outs.map(output => {
let address;
try {
- address = (0, address_1.fromOutputScript)(
- output.script,
- this.opts.network,
- );
+ address = fromOutputScript(output.script, this.opts.network);
} catch (_) {}
return {
- script: (0, bufferutils_1.cloneBuffer)(output.script),
+ script: cloneBuffer(output.script),
value: output.value,
address,
};
@@ -198,6 +223,7 @@ class Psbt {
`Requires single object with at least [hash] and [index]`,
);
}
+ checkTaprootInputFields(inputData, inputData, 'addInput');
checkInputsForPartialSig(this.data.inputs, 'addInput');
if (inputData.witnessScript) checkInvalidP2WSH(inputData.witnessScript);
const c = this.__CACHE;
@@ -234,9 +260,10 @@ class Psbt {
const { address } = outputData;
if (typeof address === 'string') {
const { network } = this.opts;
- const script = (0, address_1.toOutputScript)(address, network);
- outputData = Object.assign(outputData, { script });
+ const script = toOutputScript(address, network);
+ outputData = Object.assign({}, outputData, { script });
}
+ checkTaprootOutputFields(outputData, outputData, 'addOutput');
const c = this.__CACHE;
this.data.addOutput(outputData);
c.__FEE = undefined;
@@ -267,12 +294,37 @@ class Psbt {
return getTxCacheValue('__FEE', 'fee', this.data.inputs, this.__CACHE);
}
finalizeAllInputs() {
- (0, utils_1.checkForInput)(this.data.inputs, 0); // making sure we have at least one
+ checkForInput(this.data.inputs, 0); // making sure we have at least one
range(this.data.inputs.length).forEach(idx => this.finalizeInput(idx));
return this;
}
- finalizeInput(inputIndex, finalScriptsFunc = getFinalScripts) {
- const input = (0, utils_1.checkForInput)(this.data.inputs, inputIndex);
+ finalizeInput(inputIndex, finalScriptsFunc) {
+ const input = checkForInput(this.data.inputs, inputIndex);
+ if (isTaprootInput(input))
+ return this._finalizeTaprootInput(
+ inputIndex,
+ input,
+ undefined,
+ finalScriptsFunc,
+ );
+ return this._finalizeInput(inputIndex, input, finalScriptsFunc);
+ }
+ finalizeTaprootInput(
+ inputIndex,
+ tapLeafHashToFinalize,
+ finalScriptsFunc = tapScriptFinalizer,
+ ) {
+ const input = checkForInput(this.data.inputs, inputIndex);
+ if (isTaprootInput(input))
+ return this._finalizeTaprootInput(
+ inputIndex,
+ input,
+ tapLeafHashToFinalize,
+ finalScriptsFunc,
+ );
+ throw new Error(`Cannot finalize input #${inputIndex}. Not Taproot.`);
+ }
+ _finalizeInput(inputIndex, input, finalScriptsFunc = getFinalScripts) {
const { script, isP2SH, isP2WSH, isSegwit } = getScriptFromInput(
inputIndex,
input,
@@ -296,8 +348,37 @@ class Psbt {
this.data.clearFinalizedInput(inputIndex);
return this;
}
+ _finalizeTaprootInput(
+ inputIndex,
+ input,
+ tapLeafHashToFinalize,
+ finalScriptsFunc = tapScriptFinalizer,
+ ) {
+ if (!input.witnessUtxo)
+ throw new Error(
+ `Cannot finalize input #${inputIndex}. Missing withness utxo.`,
+ );
+ // Check key spend first. Increased privacy and reduced block space.
+ if (input.tapKeySig) {
+ const payment = payments.p2tr({
+ output: input.witnessUtxo.script,
+ signature: input.tapKeySig,
+ });
+ const finalScriptWitness = witnessStackToScriptWitness(payment.witness);
+ this.data.updateInput(inputIndex, { finalScriptWitness });
+ } else {
+ const { finalScriptWitness } = finalScriptsFunc(
+ inputIndex,
+ input,
+ tapLeafHashToFinalize,
+ );
+ this.data.updateInput(inputIndex, { finalScriptWitness });
+ }
+ this.data.clearFinalizedInput(inputIndex);
+ return this;
+ }
getInputType(inputIndex) {
- const input = (0, utils_1.checkForInput)(this.data.inputs, inputIndex);
+ const input = checkForInput(this.data.inputs, inputIndex);
const script = getScriptFromUtxo(inputIndex, input, this.__CACHE);
const result = getMeaningfulScript(
script,
@@ -312,35 +393,45 @@ class Psbt {
return type + mainType;
}
inputHasPubkey(inputIndex, pubkey) {
- const input = (0, utils_1.checkForInput)(this.data.inputs, inputIndex);
+ const input = checkForInput(this.data.inputs, inputIndex);
return pubkeyInInput(pubkey, input, inputIndex, this.__CACHE);
}
inputHasHDKey(inputIndex, root) {
- const input = (0, utils_1.checkForInput)(this.data.inputs, inputIndex);
+ const input = checkForInput(this.data.inputs, inputIndex);
const derivationIsMine = bip32DerivationIsMine(root);
return (
!!input.bip32Derivation && input.bip32Derivation.some(derivationIsMine)
);
}
outputHasPubkey(outputIndex, pubkey) {
- const output = (0, utils_1.checkForOutput)(this.data.outputs, outputIndex);
+ const output = checkForOutput(this.data.outputs, outputIndex);
return pubkeyInOutput(pubkey, output, outputIndex, this.__CACHE);
}
outputHasHDKey(outputIndex, root) {
- const output = (0, utils_1.checkForOutput)(this.data.outputs, outputIndex);
+ const output = checkForOutput(this.data.outputs, outputIndex);
const derivationIsMine = bip32DerivationIsMine(root);
return (
!!output.bip32Derivation && output.bip32Derivation.some(derivationIsMine)
);
}
validateSignaturesOfAllInputs(validator) {
- (0, utils_1.checkForInput)(this.data.inputs, 0); // making sure we have at least one
+ checkForInput(this.data.inputs, 0); // making sure we have at least one
const results = range(this.data.inputs.length).map(idx =>
this.validateSignaturesOfInput(idx, validator),
);
return results.reduce((final, res) => res === true && final, true);
}
validateSignaturesOfInput(inputIndex, validator, pubkey) {
+ const input = this.data.inputs[inputIndex];
+ if (isTaprootInput(input))
+ return this.validateSignaturesOfTaprootInput(
+ inputIndex,
+ validator,
+ pubkey,
+ );
+ return this._validateSignaturesOfInput(inputIndex, validator, pubkey);
+ }
+ _validateSignaturesOfInput(inputIndex, validator, pubkey) {
const input = this.data.inputs[inputIndex];
const partialSig = (input || {}).partialSig;
if (!input || !partialSig || partialSig.length < 1)
@@ -348,7 +439,7 @@ class Psbt {
if (typeof validator !== 'function')
throw new Error('Need validator function to validate signatures');
const mySigs = pubkey
- ? partialSig.filter(sig => sig.pubkey.equals(pubkey))
+ ? partialSig.filter(sig => tools.compare(sig.pubkey, pubkey) === 0)
: partialSig;
if (mySigs.length < 1) throw new Error('No signatures for this pubkey');
const results = [];
@@ -374,10 +465,60 @@ class Psbt {
}
return results.every(res => res === true);
}
- signAllInputsHD(
- hdKeyPair,
- sighashTypes = [transaction_1.Transaction.SIGHASH_ALL],
- ) {
+ validateSignaturesOfTaprootInput(inputIndex, validator, pubkey) {
+ const input = this.data.inputs[inputIndex];
+ const tapKeySig = (input || {}).tapKeySig;
+ const tapScriptSig = (input || {}).tapScriptSig;
+ if (!input && !tapKeySig && !(tapScriptSig && !tapScriptSig.length))
+ throw new Error('No signatures to validate');
+ if (typeof validator !== 'function')
+ throw new Error('Need validator function to validate signatures');
+ pubkey = pubkey && toXOnly(pubkey);
+ const allHashses = pubkey
+ ? getTaprootHashesForSigValidation(
+ inputIndex,
+ input,
+ this.data.inputs,
+ pubkey,
+ this.__CACHE,
+ )
+ : getAllTaprootHashesForSigValidation(
+ inputIndex,
+ input,
+ this.data.inputs,
+ this.__CACHE,
+ );
+ if (!allHashses.length) throw new Error('No signatures for this pubkey');
+ const tapKeyHash = allHashses.find(h => !h.leafHash);
+ let validationResultCount = 0;
+ if (tapKeySig && tapKeyHash) {
+ const isValidTapkeySig = validator(
+ tapKeyHash.pubkey,
+ tapKeyHash.hash,
+ trimTaprootSig(tapKeySig),
+ );
+ if (!isValidTapkeySig) return false;
+ validationResultCount++;
+ }
+ if (tapScriptSig) {
+ for (const tapSig of tapScriptSig) {
+ const tapSigHash = allHashses.find(
+ h => tools.compare(h.pubkey, tapSig.pubkey) === 0,
+ );
+ if (tapSigHash) {
+ const isValidTapScriptSig = validator(
+ tapSig.pubkey,
+ tapSigHash.hash,
+ trimTaprootSig(tapSig.signature),
+ );
+ if (!isValidTapScriptSig) return false;
+ validationResultCount++;
+ }
+ }
+ }
+ return validationResultCount > 0;
+ }
+ signAllInputsHD(hdKeyPair, sighashTypes = [Transaction.SIGHASH_ALL]) {
if (!hdKeyPair || !hdKeyPair.publicKey || !hdKeyPair.fingerprint) {
throw new Error('Need HDSigner to sign input');
}
@@ -395,10 +536,7 @@ class Psbt {
}
return this;
}
- signAllInputsHDAsync(
- hdKeyPair,
- sighashTypes = [transaction_1.Transaction.SIGHASH_ALL],
- ) {
+ signAllInputsHDAsync(hdKeyPair, sighashTypes = [Transaction.SIGHASH_ALL]) {
return new Promise((resolve, reject) => {
if (!hdKeyPair || !hdKeyPair.publicKey || !hdKeyPair.fingerprint) {
return reject(new Error('Need HDSigner to sign input'));
@@ -425,11 +563,7 @@ class Psbt {
});
});
}
- signInputHD(
- inputIndex,
- hdKeyPair,
- sighashTypes = [transaction_1.Transaction.SIGHASH_ALL],
- ) {
+ signInputHD(inputIndex, hdKeyPair, sighashTypes = [Transaction.SIGHASH_ALL]) {
if (!hdKeyPair || !hdKeyPair.publicKey || !hdKeyPair.fingerprint) {
throw new Error('Need HDSigner to sign input');
}
@@ -440,7 +574,7 @@ class Psbt {
signInputHDAsync(
inputIndex,
hdKeyPair,
- sighashTypes = [transaction_1.Transaction.SIGHASH_ALL],
+ sighashTypes = [Transaction.SIGHASH_ALL],
) {
return new Promise((resolve, reject) => {
if (!hdKeyPair || !hdKeyPair.publicKey || !hdKeyPair.fingerprint) {
@@ -457,10 +591,7 @@ class Psbt {
.catch(reject);
});
}
- signAllInputs(
- keyPair,
- sighashTypes = [transaction_1.Transaction.SIGHASH_ALL],
- ) {
+ signAllInputs(keyPair, sighashTypes) {
if (!keyPair || !keyPair.publicKey)
throw new Error('Need Signer to sign input');
// TODO: Add a pubkey/pubkeyhash cache to each input
@@ -480,10 +611,7 @@ class Psbt {
}
return this;
}
- signAllInputsAsync(
- keyPair,
- sighashTypes = [transaction_1.Transaction.SIGHASH_ALL],
- ) {
+ signAllInputsAsync(keyPair, sighashTypes) {
return new Promise((resolve, reject) => {
if (!keyPair || !keyPair.publicKey)
return reject(new Error('Need Signer to sign input'));
@@ -512,13 +640,36 @@ class Psbt {
});
});
}
- signInput(
- inputIndex,
- keyPair,
- sighashTypes = [transaction_1.Transaction.SIGHASH_ALL],
- ) {
+ signInput(inputIndex, keyPair, sighashTypes) {
if (!keyPair || !keyPair.publicKey)
throw new Error('Need Signer to sign input');
+ const input = checkForInput(this.data.inputs, inputIndex);
+ if (isTaprootInput(input)) {
+ return this._signTaprootInput(
+ inputIndex,
+ input,
+ keyPair,
+ undefined,
+ sighashTypes,
+ );
+ }
+ return this._signInput(inputIndex, keyPair, sighashTypes);
+ }
+ signTaprootInput(inputIndex, keyPair, tapLeafHashToSign, sighashTypes) {
+ if (!keyPair || !keyPair.publicKey)
+ throw new Error('Need Signer to sign input');
+ const input = checkForInput(this.data.inputs, inputIndex);
+ if (isTaprootInput(input))
+ return this._signTaprootInput(
+ inputIndex,
+ input,
+ keyPair,
+ tapLeafHashToSign,
+ sighashTypes,
+ );
+ throw new Error(`Input #${inputIndex} is not of type Taproot.`);
+ }
+ _signInput(inputIndex, keyPair, sighashTypes = [Transaction.SIGHASH_ALL]) {
const { hash, sighashType } = getHashAndSighashType(
this.data.inputs,
inputIndex,
@@ -535,32 +686,175 @@ class Psbt {
this.data.updateInput(inputIndex, { partialSig });
return this;
}
- signInputAsync(
+ _signTaprootInput(
inputIndex,
+ input,
keyPair,
- sighashTypes = [transaction_1.Transaction.SIGHASH_ALL],
+ tapLeafHashToSign,
+ allowedSighashTypes = [Transaction.SIGHASH_DEFAULT],
) {
+ const hashesForSig = this.checkTaprootHashesForSig(
+ inputIndex,
+ input,
+ keyPair,
+ tapLeafHashToSign,
+ allowedSighashTypes,
+ );
+ const tapKeySig = hashesForSig
+ .filter(h => !h.leafHash)
+ .map(h =>
+ serializeTaprootSignature(
+ keyPair.signSchnorr(h.hash),
+ input.sighashType,
+ ),
+ )[0];
+ const tapScriptSig = hashesForSig
+ .filter(h => !!h.leafHash)
+ .map(h => ({
+ pubkey: toXOnly(keyPair.publicKey),
+ signature: serializeTaprootSignature(
+ keyPair.signSchnorr(h.hash),
+ input.sighashType,
+ ),
+ leafHash: h.leafHash,
+ }));
+ if (tapKeySig) {
+ this.data.updateInput(inputIndex, { tapKeySig });
+ }
+ if (tapScriptSig.length) {
+ this.data.updateInput(inputIndex, { tapScriptSig });
+ }
+ return this;
+ }
+ signInputAsync(inputIndex, keyPair, sighashTypes) {
return Promise.resolve().then(() => {
if (!keyPair || !keyPair.publicKey)
throw new Error('Need Signer to sign input');
- const { hash, sighashType } = getHashAndSighashType(
- this.data.inputs,
- inputIndex,
- keyPair.publicKey,
- this.__CACHE,
- sighashTypes,
- );
- return Promise.resolve(keyPair.sign(hash)).then(signature => {
- const partialSig = [
- {
- pubkey: keyPair.publicKey,
- signature: bscript.signature.encode(signature, sighashType),
+ const input = checkForInput(this.data.inputs, inputIndex);
+ if (isTaprootInput(input))
+ return this._signTaprootInputAsync(
+ inputIndex,
+ input,
+ keyPair,
+ undefined,
+ sighashTypes,
+ );
+ return this._signInputAsync(inputIndex, keyPair, sighashTypes);
+ });
+ }
+ signTaprootInputAsync(inputIndex, keyPair, tapLeafHash, sighashTypes) {
+ return Promise.resolve().then(() => {
+ if (!keyPair || !keyPair.publicKey)
+ throw new Error('Need Signer to sign input');
+ const input = checkForInput(this.data.inputs, inputIndex);
+ if (isTaprootInput(input))
+ return this._signTaprootInputAsync(
+ inputIndex,
+ input,
+ keyPair,
+ tapLeafHash,
+ sighashTypes,
+ );
+ throw new Error(`Input #${inputIndex} is not of type Taproot.`);
+ });
+ }
+ _signInputAsync(
+ inputIndex,
+ keyPair,
+ sighashTypes = [Transaction.SIGHASH_ALL],
+ ) {
+ const { hash, sighashType } = getHashAndSighashType(
+ this.data.inputs,
+ inputIndex,
+ keyPair.publicKey,
+ this.__CACHE,
+ sighashTypes,
+ );
+ return Promise.resolve(keyPair.sign(hash)).then(signature => {
+ const partialSig = [
+ {
+ pubkey: keyPair.publicKey,
+ signature: bscript.signature.encode(signature, sighashType),
+ },
+ ];
+ this.data.updateInput(inputIndex, { partialSig });
+ });
+ }
+ async _signTaprootInputAsync(
+ inputIndex,
+ input,
+ keyPair,
+ tapLeafHash,
+ sighashTypes = [Transaction.SIGHASH_DEFAULT],
+ ) {
+ const hashesForSig = this.checkTaprootHashesForSig(
+ inputIndex,
+ input,
+ keyPair,
+ tapLeafHash,
+ sighashTypes,
+ );
+ const signaturePromises = [];
+ const tapKeyHash = hashesForSig.filter(h => !h.leafHash)[0];
+ if (tapKeyHash) {
+ const tapKeySigPromise = Promise.resolve(
+ keyPair.signSchnorr(tapKeyHash.hash),
+ ).then(sig => {
+ return { tapKeySig: serializeTaprootSignature(sig, input.sighashType) };
+ });
+ signaturePromises.push(tapKeySigPromise);
+ }
+ const tapScriptHashes = hashesForSig.filter(h => !!h.leafHash);
+ if (tapScriptHashes.length) {
+ const tapScriptSigPromises = tapScriptHashes.map(tsh => {
+ return Promise.resolve(keyPair.signSchnorr(tsh.hash)).then(
+ signature => {
+ const tapScriptSig = [
+ {
+ pubkey: toXOnly(keyPair.publicKey),
+ signature: serializeTaprootSignature(
+ signature,
+ input.sighashType,
+ ),
+ leafHash: tsh.leafHash,
+ },
+ ];
+ return { tapScriptSig };
},
- ];
- this.data.updateInput(inputIndex, { partialSig });
+ );
});
+ signaturePromises.push(...tapScriptSigPromises);
+ }
+ return Promise.all(signaturePromises).then(results => {
+ results.forEach(v => this.data.updateInput(inputIndex, v));
});
}
+ checkTaprootHashesForSig(
+ inputIndex,
+ input,
+ keyPair,
+ tapLeafHashToSign,
+ allowedSighashTypes,
+ ) {
+ if (typeof keyPair.signSchnorr !== 'function')
+ throw new Error(
+ `Need Schnorr Signer to sign taproot input #${inputIndex}.`,
+ );
+ const hashesForSig = getTaprootHashesForSigning(
+ inputIndex,
+ input,
+ this.data.inputs,
+ keyPair.publicKey,
+ this.__CACHE,
+ tapLeafHashToSign,
+ allowedSighashTypes,
+ );
+ if (!hashesForSig || !hashesForSig.length)
+ throw new Error(
+ `Can not sign for input #${inputIndex} with the key ${tools.toHex(keyPair.publicKey)}`,
+ );
+ return hashesForSig;
+ }
toBuffer() {
checkCache(this.__CACHE);
return this.data.toBuffer();
@@ -579,6 +873,11 @@ class Psbt {
}
updateInput(inputIndex, updateData) {
if (updateData.witnessScript) checkInvalidP2WSH(updateData.witnessScript);
+ checkTaprootInputFields(
+ this.data.inputs[inputIndex],
+ updateData,
+ 'updateInput',
+ );
this.data.updateInput(inputIndex, updateData);
if (updateData.nonWitnessUtxo) {
addNonWitnessTxCache(
@@ -590,6 +889,8 @@ class Psbt {
return this;
}
updateOutput(outputIndex, updateData) {
+ const outputData = this.data.outputs[outputIndex];
+ checkTaprootOutputFields(outputData, updateData, 'updateOutput');
this.data.updateOutput(outputIndex, updateData);
return this;
}
@@ -610,7 +911,6 @@ class Psbt {
return this;
}
}
-exports.Psbt = Psbt;
/**
* This function is needed to pass to the bip174 base class's fromBuffer.
* It takes the "transaction buffer" portion of the psbt buffer and returns a
@@ -622,8 +922,9 @@ const transactionFromBuffer = buffer => new PsbtTransaction(buffer);
* It contains a bitcoinjs-lib Transaction object.
*/
class PsbtTransaction {
- constructor(buffer = Buffer.from([2, 0, 0, 0, 0, 0, 0, 0, 0, 0])) {
- this.tx = transaction_1.Transaction.fromBuffer(buffer);
+ tx;
+ constructor(buffer = Uint8Array.from([2, 0, 0, 0, 0, 0, 0, 0, 0, 0])) {
+ this.tx = Transaction.fromBuffer(buffer);
checkTxEmpty(this.tx);
Object.defineProperty(this, 'tx', {
enumerable: false,
@@ -640,14 +941,14 @@ class PsbtTransaction {
if (
input.hash === undefined ||
input.index === undefined ||
- (!Buffer.isBuffer(input.hash) && typeof input.hash !== 'string') ||
+ (!(input.hash instanceof Uint8Array) && typeof input.hash !== 'string') ||
typeof input.index !== 'number'
) {
throw new Error('Error adding input.');
}
const hash =
typeof input.hash === 'string'
- ? (0, bufferutils_1.reverseBuffer)(Buffer.from(input.hash, 'hex'))
+ ? reverseBuffer(tools.fromHex(input.hash))
: input.hash;
this.tx.addInput(hash, input.index, input.sequence);
}
@@ -655,8 +956,8 @@ class PsbtTransaction {
if (
output.script === undefined ||
output.value === undefined ||
- !Buffer.isBuffer(output.script) ||
- typeof output.value !== 'number'
+ !(output.script instanceof Uint8Array) ||
+ typeof output.value !== 'bigint'
) {
throw new Error('Error adding output.');
}
@@ -691,7 +992,9 @@ function hasSigs(neededSigs, partialSig, pubkeys) {
sigs = pubkeys
.map(pkey => {
const pubkey = compressPubkey(pkey);
- return partialSig.find(pSig => pSig.pubkey.equals(pubkey));
+ return partialSig.find(
+ pSig => tools.compare(pSig.pubkey, pubkey) === 0,
+ );
})
.filter(v => !!v);
} else {
@@ -703,26 +1006,11 @@ function hasSigs(neededSigs, partialSig, pubkeys) {
function isFinalized(input) {
return !!input.finalScriptSig || !!input.finalScriptWitness;
}
-function isPaymentFactory(payment) {
- return script => {
- try {
- payment({ output: script });
- return true;
- } catch (err) {
- return false;
- }
- };
-}
-const isP2MS = isPaymentFactory(payments.p2ms);
-const isP2PK = isPaymentFactory(payments.p2pk);
-const isP2PKH = isPaymentFactory(payments.p2pkh);
-const isP2WPKH = isPaymentFactory(payments.p2wpkh);
-const isP2WSHScript = isPaymentFactory(payments.p2wsh);
-const isP2SHScript = isPaymentFactory(payments.p2sh);
function bip32DerivationIsMine(root) {
return d => {
- if (!d.masterFingerprint.equals(root.fingerprint)) return false;
- if (!root.derivePath(d.path).publicKey.equals(d.pubkey)) return false;
+ if (tools.compare(root.fingerprint, d.masterFingerprint)) return false;
+ if (tools.compare(root.derivePath(d.path).publicKey, d.pubkey))
+ return false;
return true;
};
}
@@ -752,37 +1040,11 @@ function checkFees(psbt, cache, opts) {
}
function checkInputsForPartialSig(inputs, action) {
inputs.forEach(input => {
- let throws = false;
- let pSigs = [];
- if ((input.partialSig || []).length === 0) {
- if (!input.finalScriptSig && !input.finalScriptWitness) return;
- pSigs = getPsigsFromInputFinalScripts(input);
- } else {
- pSigs = input.partialSig;
- }
- pSigs.forEach(pSig => {
- const { hashType } = bscript.signature.decode(pSig.signature);
- const whitelist = [];
- const isAnyoneCanPay =
- hashType & transaction_1.Transaction.SIGHASH_ANYONECANPAY;
- if (isAnyoneCanPay) whitelist.push('addInput');
- const hashMod = hashType & 0x1f;
- switch (hashMod) {
- case transaction_1.Transaction.SIGHASH_ALL:
- break;
- case transaction_1.Transaction.SIGHASH_SINGLE:
- case transaction_1.Transaction.SIGHASH_NONE:
- whitelist.push('addOutput');
- whitelist.push('setInputSequence');
- break;
- }
- if (whitelist.indexOf(action) === -1) {
- throws = true;
- }
- });
- if (throws) {
+ const throws = isTaprootInput(input)
+ ? checkTaprootInputForSigs(input, action)
+ : checkInputForSig(input, action);
+ if (throws)
throw new Error('Can not modify transaction, signatures exist.');
- }
});
}
function checkPartialSigSighashes(input) {
@@ -798,7 +1060,7 @@ function checkPartialSigSighashes(input) {
function checkScriptForPubkey(pubkey, script, action) {
if (!pubkeyInScript(pubkey, script)) {
throw new Error(
- `Can not ${action} for this input with the key ${pubkey.toString('hex')}`,
+ `Can not ${action} for this input with the key ${tools.toHex(pubkey)}`,
);
}
}
@@ -821,9 +1083,7 @@ function checkTxForDupeIns(tx, cache) {
}
function checkTxInputCache(cache, input) {
const key =
- (0, bufferutils_1.reverseBuffer)(Buffer.from(input.hash)).toString('hex') +
- ':' +
- input.index;
+ tools.toHex(reverseBuffer(Uint8Array.from(input.hash))) + ':' + input.index;
if (cache.__TX_IN_CACHE[key]) throw new Error('Duplicate input detected.');
cache.__TX_IN_CACHE[key] = 1;
}
@@ -832,7 +1092,7 @@ function scriptCheckerFactory(payment, paymentScriptName) {
const redeemScriptOutput = payment({
redeem: { output: redeemScript },
}).output;
- if (!scriptPubKey.equals(redeemScriptOutput)) {
+ if (tools.compare(scriptPubKey, redeemScriptOutput)) {
throw new Error(
`${paymentScriptName} for ${ioType} #${inputIndex} doesn't match the scriptPubKey in the prevout`,
);
@@ -916,7 +1176,7 @@ function getHashAndSighashType(
cache,
sighashTypes,
) {
- const input = (0, utils_1.checkForInput)(inputs, inputIndex);
+ const input = checkForInput(inputs, inputIndex);
const { hash, sighashType, script } = getHashForSig(
inputIndex,
input,
@@ -932,15 +1192,8 @@ function getHashAndSighashType(
}
function getHashForSig(inputIndex, input, cache, forValidate, sighashTypes) {
const unsignedTx = cache.__TX;
- const sighashType =
- input.sighashType || transaction_1.Transaction.SIGHASH_ALL;
- if (sighashTypes && sighashTypes.indexOf(sighashType) < 0) {
- const str = sighashTypeToString(sighashType);
- throw new Error(
- `Sighash type is not allowed. Retry the sign method passing the ` +
- `sighashTypes array of whitelisted types. Sighash type: ${str}`,
- );
- }
+ const sighashType = input.sighashType || Transaction.SIGHASH_ALL;
+ checkSighashTypeAllowed(sighashType, sighashTypes);
let hash;
let prevout;
if (input.nonWitnessUtxo) {
@@ -952,7 +1205,7 @@ function getHashForSig(inputIndex, input, cache, forValidate, sighashTypes) {
const prevoutHash = unsignedTx.ins[inputIndex].hash;
const utxoHash = nonWitnessUtxoTx.getHash();
// If a non-witness UTXO is provided, its hash must match the hash specified in the prevout
- if (!prevoutHash.equals(utxoHash)) {
+ if (tools.compare(prevoutHash, utxoHash) !== 0) {
throw new Error(
`Non-witness UTXO hash for input #${inputIndex} doesn't match the hash specified in the prevout`,
);
@@ -980,8 +1233,9 @@ function getHashForSig(inputIndex, input, cache, forValidate, sighashTypes) {
);
} else if (isP2WPKH(meaningfulScript)) {
// P2WPKH uses the P2PKH template for prevoutScript when signing
- const signingScript = payments.p2pkh({ hash: meaningfulScript.slice(2) })
- .output;
+ const signingScript = payments.p2pkh({
+ hash: meaningfulScript.slice(2),
+ }).output;
hash = unsignedTx.hashForWitnessV0(
inputIndex,
signingScript,
@@ -996,13 +1250,13 @@ function getHashForSig(inputIndex, input, cache, forValidate, sighashTypes) {
)
throw new Error(
`Input #${inputIndex} has witnessUtxo but non-segwit script: ` +
- `${meaningfulScript.toString('hex')}`,
+ `${tools.toHex(meaningfulScript)}`,
);
if (!forValidate && cache.__UNSAFE_SIGN_NONSEGWIT !== false)
console.warn(
'Warning: Signing non-segwit inputs without the full parent transaction ' +
'means there is a chance that a miner could feed you incorrect information ' +
- "to trick you into paying large fees. This behavior is the same as Psbt's predecesor " +
+ "to trick you into paying large fees. This behavior is the same as Psbt's predecessor " +
'(TransactionBuilder - now removed) when signing non-segwit scripts. You are not ' +
'able to export this Psbt with toBuffer|toBase64|toHex since it is not ' +
'BIP174 compliant.\n*********************\nPROCEED WITH CAUTION!\n' +
@@ -1020,6 +1274,152 @@ function getHashForSig(inputIndex, input, cache, forValidate, sighashTypes) {
hash,
};
}
+function getAllTaprootHashesForSigValidation(inputIndex, input, inputs, cache) {
+ const allPublicKeys = [];
+ if (input.tapInternalKey) {
+ const key = getPrevoutTaprootKey(inputIndex, input, cache);
+ if (key) {
+ allPublicKeys.push(key);
+ }
+ }
+ if (input.tapScriptSig) {
+ const tapScriptPubkeys = input.tapScriptSig.map(tss => tss.pubkey);
+ allPublicKeys.push(...tapScriptPubkeys);
+ }
+ const allHashes = allPublicKeys.map(publicKey =>
+ getTaprootHashesForSigValidation(
+ inputIndex,
+ input,
+ inputs,
+ publicKey,
+ cache,
+ ),
+ );
+ return allHashes.flat();
+}
+function getPrevoutTaprootKey(inputIndex, input, cache) {
+ const { script } = getScriptAndAmountFromUtxo(inputIndex, input, cache);
+ return isP2TR(script) ? script.subarray(2, 34) : null;
+}
+function trimTaprootSig(signature) {
+ return signature.length === 64 ? signature : signature.subarray(0, 64);
+}
+function getTaprootHashesForSigning(
+ inputIndex,
+ input,
+ inputs,
+ pubkey,
+ cache,
+ tapLeafHashToSign,
+ allowedSighashTypes,
+) {
+ const sighashType = input.sighashType || Transaction.SIGHASH_DEFAULT;
+ checkSighashTypeAllowed(sighashType, allowedSighashTypes);
+ const keySpend = Boolean(input.tapInternalKey && !tapLeafHashToSign);
+ return getTaprootHashesForSig(
+ inputIndex,
+ input,
+ inputs,
+ pubkey,
+ cache,
+ keySpend,
+ sighashType,
+ tapLeafHashToSign,
+ );
+}
+function getTaprootHashesForSigValidation(
+ inputIndex,
+ input,
+ inputs,
+ pubkey,
+ cache,
+) {
+ const sighashType = input.sighashType || Transaction.SIGHASH_DEFAULT;
+ const keySpend = Boolean(input.tapKeySig);
+ return getTaprootHashesForSig(
+ inputIndex,
+ input,
+ inputs,
+ pubkey,
+ cache,
+ keySpend,
+ sighashType,
+ );
+}
+/*
+ * This helper method is used for both generating a hash for signing as well for validating;
+ * thus depending on context key spend can be detected either with tapInternalKey with no leaves
+ * or tapKeySig (note tapKeySig is a signature to the prevout pk, so tapInternalKey is not needed).
+ */
+function getTaprootHashesForSig(
+ inputIndex,
+ input,
+ inputs,
+ pubkey,
+ cache,
+ keySpend,
+ sighashType,
+ tapLeafHashToSign,
+) {
+ const unsignedTx = cache.__TX;
+ const prevOuts = inputs.map((i, index) =>
+ getScriptAndAmountFromUtxo(index, i, cache),
+ );
+ const signingScripts = prevOuts.map(o => o.script);
+ const values = prevOuts.map(o => o.value);
+ const hashes = [];
+ if (keySpend) {
+ const outputKey =
+ getPrevoutTaprootKey(inputIndex, input, cache) || Uint8Array.from([]);
+ if (tools.compare(toXOnly(pubkey), outputKey) === 0) {
+ const tapKeyHash = unsignedTx.hashForWitnessV1(
+ inputIndex,
+ signingScripts,
+ values,
+ sighashType,
+ );
+ hashes.push({ pubkey, hash: tapKeyHash });
+ }
+ }
+ const tapLeafHashes = (input.tapLeafScript || [])
+ .filter(tapLeaf => pubkeyInScript(pubkey, tapLeaf.script))
+ .map(tapLeaf => {
+ const hash = tapleafHash({
+ output: tapLeaf.script,
+ version: tapLeaf.leafVersion,
+ });
+ return Object.assign({ hash }, tapLeaf);
+ })
+ .filter(
+ tapLeaf =>
+ !tapLeafHashToSign ||
+ tools.compare(tapLeafHashToSign, tapLeaf.hash) === 0,
+ )
+ .map(tapLeaf => {
+ const tapScriptHash = unsignedTx.hashForWitnessV1(
+ inputIndex,
+ signingScripts,
+ values,
+ sighashType,
+ tapLeaf.hash,
+ );
+ return {
+ pubkey,
+ hash: tapScriptHash,
+ leafHash: tapLeaf.hash,
+ };
+ });
+ return hashes.concat(tapLeafHashes);
+}
+function checkSighashTypeAllowed(sighashType, sighashTypes) {
+ if (sighashTypes && sighashTypes.indexOf(sighashType) < 0) {
+ const str = sighashTypeToString(sighashType);
+ throw new Error(
+ `Sighash type is not allowed. Retry the sign method passing the ` +
+ `sighashTypes array of whitelisted types. Sighash type: ${str}`,
+ );
+ }
+}
function getPayment(script, scriptType, partialSig) {
let payment;
switch (scriptType) {
@@ -1053,20 +1453,6 @@ function getPayment(script, scriptType, partialSig) {
}
return payment;
}
-function getPsigsFromInputFinalScripts(input) {
- const scriptItems = !input.finalScriptSig
- ? []
- : bscript.decompile(input.finalScriptSig) || [];
- const witnessItems = !input.finalScriptWitness
- ? []
- : bscript.decompile(input.finalScriptWitness) || [];
- return scriptItems
- .concat(witnessItems)
- .filter(item => {
- return Buffer.isBuffer(item) && bscript.isCanonicalScriptSignature(item);
- })
- .map(sig => ({ signature: sig }));
-}
function getScriptFromInput(inputIndex, input, cache) {
const unsignedTx = cache.__TX;
const res = {
@@ -1100,13 +1486,13 @@ function getScriptFromInput(inputIndex, input, cache) {
return res;
}
function getSignersFromHD(inputIndex, inputs, hdKeyPair) {
- const input = (0, utils_1.checkForInput)(inputs, inputIndex);
+ const input = checkForInput(inputs, inputIndex);
if (!input.bip32Derivation || input.bip32Derivation.length === 0) {
throw new Error('Need bip32Derivation to sign with HD');
}
const myDerivations = input.bip32Derivation
.map(bipDv => {
- if (bipDv.masterFingerprint.equals(hdKeyPair.fingerprint)) {
+ if (tools.compare(bipDv.masterFingerprint, hdKeyPair.fingerprint) === 0) {
return bipDv;
} else {
return;
@@ -1120,7 +1506,7 @@ function getSignersFromHD(inputIndex, inputs, hdKeyPair) {
}
const signers = myDerivations.map(bipDv => {
const node = hdKeyPair.derivePath(bipDv.path);
- if (!bipDv.pubkey.equals(node.publicKey)) {
+ if (tools.compare(bipDv.pubkey, node.publicKey) !== 0) {
throw new Error('pubkey did not match bip32Derivation');
}
return node;
@@ -1135,7 +1521,7 @@ function getSortedSigs(script, partialSig) {
// filter partialSig array by pubkey being equal
return (
partialSig.filter(ps => {
- return ps.pubkey.equals(pk);
+ return tools.compare(ps.pubkey, pk) === 0;
})[0] || {}
).signature;
// Any pubkey without a match will return undefined
@@ -1151,8 +1537,8 @@ function scriptWitnessToWitnessStack(buffer) {
}
function readVarInt() {
const vi = varuint.decode(buffer, offset);
- offset += varuint.decode.bytes;
- return vi;
+ offset += varuint.encodingLength(vi.bigintValue);
+ return vi.numberValue;
}
function readVarSlice() {
return readSlice(readVarInt());
@@ -1167,48 +1553,26 @@ function scriptWitnessToWitnessStack(buffer) {
}
function sighashTypeToString(sighashType) {
let text =
- sighashType & transaction_1.Transaction.SIGHASH_ANYONECANPAY
+ sighashType & Transaction.SIGHASH_ANYONECANPAY
? 'SIGHASH_ANYONECANPAY | '
: '';
const sigMod = sighashType & 0x1f;
switch (sigMod) {
- case transaction_1.Transaction.SIGHASH_ALL:
+ case Transaction.SIGHASH_ALL:
text += 'SIGHASH_ALL';
break;
- case transaction_1.Transaction.SIGHASH_SINGLE:
+ case Transaction.SIGHASH_SINGLE:
text += 'SIGHASH_SINGLE';
break;
- case transaction_1.Transaction.SIGHASH_NONE:
+ case Transaction.SIGHASH_NONE:
text += 'SIGHASH_NONE';
break;
}
return text;
}
-function witnessStackToScriptWitness(witness) {
- let buffer = Buffer.allocUnsafe(0);
- function writeSlice(slice) {
- buffer = Buffer.concat([buffer, Buffer.from(slice)]);
- }
- function writeVarInt(i) {
- const currentLen = buffer.length;
- const varintLen = varuint.encodingLength(i);
- buffer = Buffer.concat([buffer, Buffer.allocUnsafe(varintLen)]);
- varuint.encode(i, buffer, currentLen);
- }
- function writeVarSlice(slice) {
- writeVarInt(slice.length);
- writeSlice(slice);
- }
- function writeVector(vector) {
- writeVarInt(vector.length);
- vector.forEach(writeVarSlice);
- }
- writeVector(witness);
- return buffer;
-}
function addNonWitnessTxCache(cache, input, inputIndex) {
cache.__NON_WITNESS_UTXO_BUF_CACHE[inputIndex] = input.nonWitnessUtxo;
- const tx = transaction_1.Transaction.fromBuffer(input.nonWitnessUtxo);
+ const tx = Transaction.fromBuffer(input.nonWitnessUtxo);
cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex] = tx;
const self = cache;
const selfIndex = inputIndex;
@@ -1232,7 +1596,7 @@ function addNonWitnessTxCache(cache, input, inputIndex) {
});
}
function inputFinalizeGetAmts(inputs, tx, cache, mustFinalize) {
- let inputAmount = 0;
+ let inputAmount = 0n;
inputs.forEach((input, idx) => {
if (mustFinalize && input.finalScriptSig)
tx.ins[idx].script = input.finalScriptSig;
@@ -1250,7 +1614,7 @@ function inputFinalizeGetAmts(inputs, tx, cache, mustFinalize) {
inputAmount += out.value;
}
});
- const outputAmount = tx.outs.reduce((total, o) => total + o.value, 0);
+ const outputAmount = tx.outs.reduce((total, o) => total + o.value, 0n);
const fee = inputAmount - outputAmount;
if (fee < 0) {
throw new Error('Outputs are spending more than Inputs');
@@ -1258,7 +1622,7 @@ function inputFinalizeGetAmts(inputs, tx, cache, mustFinalize) {
const bytes = tx.virtualSize();
cache.__FEE = fee;
cache.__EXTRACTED_TX = tx;
- cache.__FEE_RATE = Math.floor(fee / bytes);
+ cache.__FEE_RATE = Math.floor(Number(fee / BigInt(bytes)));
}
function nonWitnessUtxoTxFromCache(cache, input, inputIndex) {
const c = cache.__NON_WITNESS_UTXO_TX_CACHE;
@@ -1268,15 +1632,23 @@ function nonWitnessUtxoTxFromCache(cache, input, inputIndex) {
return c[inputIndex];
}
function getScriptFromUtxo(inputIndex, input, cache) {
+ const { script } = getScriptAndAmountFromUtxo(inputIndex, input, cache);
+ return script;
+}
+function getScriptAndAmountFromUtxo(inputIndex, input, cache) {
if (input.witnessUtxo !== undefined) {
- return input.witnessUtxo.script;
+ return {
+ script: input.witnessUtxo.script,
+ value: input.witnessUtxo.value,
+ };
} else if (input.nonWitnessUtxo !== undefined) {
const nonWitnessUtxoTx = nonWitnessUtxoTxFromCache(
cache,
input,
inputIndex,
);
- return nonWitnessUtxoTx.outs[cache.__TX.ins[inputIndex].index].script;
+ const o = nonWitnessUtxoTx.outs[cache.__TX.ins[inputIndex].index];
+ return { script: o.script, value: o.value };
} else {
throw new Error("Can't find pubkey in input without Utxo data");
}
@@ -1309,7 +1681,7 @@ function redeemFromFinalScriptSig(finalScript) {
if (!decomp) return;
const lastItem = decomp[decomp.length - 1];
if (
- !Buffer.isBuffer(lastItem) ||
+ !(lastItem instanceof Uint8Array) ||
isPubkeyLike(lastItem) ||
isSigLike(lastItem)
)
@@ -1379,10 +1751,10 @@ function getMeaningfulScript(
type: isP2SHP2WSH
? 'p2sh-p2wsh'
: isP2SH
- ? 'p2sh'
- : isP2WSH
- ? 'p2wsh'
- : 'raw',
+ ? 'p2sh'
+ : isP2WSH
+ ? 'p2wsh'
+ : 'raw',
};
}
function checkInvalidP2WSH(script) {
@@ -1390,15 +1762,6 @@ function checkInvalidP2WSH(script) {
throw new Error('P2WPKH or P2SH can not be contained within P2WSH');
}
}
-function pubkeyInScript(pubkey, script) {
- const pubkeyHash = (0, crypto_1.hash160)(pubkey);
- const decompiled = bscript.decompile(script);
- if (decompiled === null) throw new Error('Unknown script error');
- return decompiled.some(element => {
- if (typeof element === 'number') return false;
- return element.equals(pubkey) || element.equals(pubkeyHash);
- });
-}
function classifyScript(script) {
if (isP2WPKH(script)) return 'witnesspubkeyhash';
if (isP2PKH(script)) return 'pubkeyhash';
diff --git a/src/esm/psbt/bip371.js b/src/esm/psbt/bip371.js
new file mode 100644
index 000000000..dd40bbfeb
--- /dev/null
+++ b/src/esm/psbt/bip371.js
@@ -0,0 +1,490 @@
+import { isTapleaf, isTaptree } from '../types.js';
+import { Transaction } from '../transaction.js';
+import {
+ witnessStackToScriptWitness,
+ pubkeyPositionInScript,
+ isP2TR,
+} from './psbtutils.js';
+import {
+ tweakKey,
+ tapleafHash,
+ rootHashFromPath,
+ LEAF_VERSION_TAPSCRIPT,
+ MAX_TAPTREE_DEPTH,
+} from '../payments/bip341.js';
+import { p2tr } from '../payments/index.js';
+import * as tools from 'uint8array-tools';
+import { signatureBlocksAction } from './psbtutils.js';
+/**
+ * Converts a public key to an X-only public key.
+ * @param pubKey The public key to convert.
+ * @returns The X-only public key.
+ */
+export const toXOnly = pubKey =>
+ pubKey.length === 32 ? pubKey : pubKey.slice(1, 33);
+/**
+ * Default tapscript finalizer. It searches for the `tapLeafHashToFinalize` if provided.
+ * Otherwise it will search for the tapleaf that has at least one signature and has the shortest path.
+ * @param inputIndex the position of the PSBT input.
+ * @param input the PSBT input.
+ * @param tapLeafHashToFinalize optional, if provided the finalizer will search for a tapleaf that has this hash
+ * and will try to build the finalScriptWitness.
+ * @returns the finalScriptWitness or throws an exception if no tapleaf found.
+ */
+export function tapScriptFinalizer(inputIndex, input, tapLeafHashToFinalize) {
+ const tapLeaf = findTapLeafToFinalize(
+ input,
+ inputIndex,
+ tapLeafHashToFinalize,
+ );
+ try {
+ const sigs = sortSignatures(input, tapLeaf);
+ const witness = sigs.concat(tapLeaf.script).concat(tapLeaf.controlBlock);
+ return { finalScriptWitness: witnessStackToScriptWitness(witness) };
+ } catch (err) {
+ throw new Error(`Can not finalize taproot input #${inputIndex}: ${err}`);
+ }
+}
+/**
+ * Serializes a taproot signature.
+ * @param sig The signature to serialize.
+ * @param sighashType The sighash type. Optional.
+ * @returns The serialized taproot signature.
+ */
+export function serializeTaprootSignature(sig, sighashType) {
+ const sighashTypeByte = sighashType
+ ? Uint8Array.from([sighashType])
+ : Uint8Array.from([]);
+ return tools.concat([sig, sighashTypeByte]);
+}
+/**
+ * Checks if a PSBT input is a taproot input.
+ * @param input The PSBT input to check.
+ * @returns True if the input is a taproot input, false otherwise.
+ */
+export function isTaprootInput(input) {
+ return (
+ input &&
+ !!(
+ input.tapInternalKey ||
+ input.tapMerkleRoot ||
+ (input.tapLeafScript && input.tapLeafScript.length) ||
+ (input.tapBip32Derivation && input.tapBip32Derivation.length) ||
+ (input.witnessUtxo && isP2TR(input.witnessUtxo.script))
+ )
+ );
+}
+/**
+ * Checks if a PSBT output is a taproot output.
+ * @param output The PSBT output to check.
+ * @param script The script to check. Optional.
+ * @returns True if the output is a taproot output, false otherwise.
+ */
+export function isTaprootOutput(output, script) {
+ return (
+ output &&
+ !!(
+ output.tapInternalKey ||
+ output.tapTree ||
+ (output.tapBip32Derivation && output.tapBip32Derivation.length) ||
+ (script && isP2TR(script))
+ )
+ );
+}
+/**
+ * Checks the taproot input fields for consistency.
+ * @param inputData The original input data.
+ * @param newInputData The new input data.
+ * @param action The action being performed.
+ * @throws Throws an error if the input fields are inconsistent.
+ */
+export function checkTaprootInputFields(inputData, newInputData, action) {
+ checkMixedTaprootAndNonTaprootInputFields(inputData, newInputData, action);
+ checkIfTapLeafInTree(inputData, newInputData, action);
+}
+/**
+ * Checks the taproot output fields for consistency.
+ * @param outputData The original output data.
+ * @param newOutputData The new output data.
+ * @param action The action being performed.
+ * @throws Throws an error if the output fields are inconsistent.
+ */
+export function checkTaprootOutputFields(outputData, newOutputData, action) {
+ checkMixedTaprootAndNonTaprootOutputFields(outputData, newOutputData, action);
+ checkTaprootScriptPubkey(outputData, newOutputData);
+}
+function checkTaprootScriptPubkey(outputData, newOutputData) {
+ if (!newOutputData.tapTree && !newOutputData.tapInternalKey) return;
+ const tapInternalKey =
+ newOutputData.tapInternalKey || outputData.tapInternalKey;
+ const tapTree = newOutputData.tapTree || outputData.tapTree;
+ if (tapInternalKey) {
+ const { script: scriptPubkey } = outputData;
+ const script = getTaprootScripPubkey(tapInternalKey, tapTree);
+ if (scriptPubkey && tools.compare(script, scriptPubkey) !== 0)
+ throw new Error('Error adding output. Script or address mismatch.');
+ }
+}
+/**
+ * Returns the Taproot script public key.
+ *
+ * @param tapInternalKey - The Taproot internal key.
+ * @param tapTree - The Taproot tree (optional).
+ * @returns The Taproot script public key.
+ */
+function getTaprootScripPubkey(tapInternalKey, tapTree) {
+ const scriptTree = tapTree && tapTreeFromList(tapTree.leaves);
+ const { output } = p2tr({
+ internalPubkey: tapInternalKey,
+ scriptTree,
+ });
+ return output;
+}
+/**
+ * Tweak the internal public key for a specific input.
+ * @param inputIndex - The index of the input.
+ * @param input - The PsbtInput object representing the input.
+ * @returns The tweaked internal public key.
+ * @throws Error if the tap internal key cannot be tweaked.
+ */
+export function tweakInternalPubKey(inputIndex, input) {
+ const tapInternalKey = input.tapInternalKey;
+ const outputKey =
+ tapInternalKey && tweakKey(tapInternalKey, input.tapMerkleRoot);
+ if (!outputKey)
+ throw new Error(
+ `Cannot tweak tap internal key for input #${inputIndex}. Public key: ${
+ // tapInternalKey && tapInternalKey.toString('hex')
+ tapInternalKey && tools.toHex(tapInternalKey)
+ }`,
+ );
+ return outputKey.x;
+}
+/**
+ * Convert a binary tree to a BIP371 type list. Each element of the list is (according to BIP371):
+ * One or more tuples representing the depth, leaf version, and script for a leaf in the Taproot tree,
+ * allowing the entire tree to be reconstructed. The tuples must be in depth first search order so that
+ * the tree is correctly reconstructed.
+ * @param tree the binary tap tree
+ * @returns a list of BIP 371 tapleaves
+ */
+export function tapTreeToList(tree) {
+ if (!isTaptree(tree))
+ throw new Error(
+ 'Cannot convert taptree to tapleaf list. Expecting a tapree structure.',
+ );
+ return _tapTreeToList(tree);
+}
+/**
+ * Convert a BIP371 TapLeaf list to a TapTree (binary).
+ * @param leaves a list of tapleaves where each element of the list is (according to BIP371):
+ * One or more tuples representing the depth, leaf version, and script for a leaf in the Taproot tree,
+ * allowing the entire tree to be reconstructed. The tuples must be in depth first search order so that
+ * the tree is correctly reconstructed.
+ * @returns the corresponding taptree, or throws an exception if the tree cannot be reconstructed
+ */
+export function tapTreeFromList(leaves = []) {
+ if (leaves.length === 1 && leaves[0].depth === 0)
+ return {
+ output: leaves[0].script,
+ version: leaves[0].leafVersion,
+ };
+ return instertLeavesInTree(leaves);
+}
+/**
+ * Checks the taproot input for signatures.
+ * @param input The PSBT input to check.
+ * @param action The action being performed.
+ * @returns True if the input has taproot signatures, false otherwise.
+ */
+export function checkTaprootInputForSigs(input, action) {
+ const sigs = extractTaprootSigs(input);
+ return sigs.some(sig =>
+ signatureBlocksAction(sig, decodeSchnorrSignature, action),
+ );
+}
+/**
+ * Decodes a Schnorr signature.
+ * @param signature The signature to decode.
+ * @returns The decoded Schnorr signature.
+ */
+function decodeSchnorrSignature(signature) {
+ return {
+ signature: signature.slice(0, 64),
+ hashType: signature.slice(64)[0] || Transaction.SIGHASH_DEFAULT,
+ };
+}
+/**
+ * Extracts taproot signatures from a PSBT input.
+ * @param input The PSBT input to extract signatures from.
+ * @returns An array of taproot signatures.
+ */
+function extractTaprootSigs(input) {
+ const sigs = [];
+ if (input.tapKeySig) sigs.push(input.tapKeySig);
+ if (input.tapScriptSig)
+ sigs.push(...input.tapScriptSig.map(s => s.signature));
+ if (!sigs.length) {
+ const finalTapKeySig = getTapKeySigFromWitness(input.finalScriptWitness);
+ if (finalTapKeySig) sigs.push(finalTapKeySig);
+ }
+ return sigs;
+}
+/**
+ * Gets the taproot signature from the witness.
+ * @param finalScriptWitness The final script witness.
+ * @returns The taproot signature, or undefined if not found.
+ */
+function getTapKeySigFromWitness(finalScriptWitness) {
+ if (!finalScriptWitness) return;
+ const witness = finalScriptWitness.slice(2);
+ // todo: add schnorr signature validation
+ if (witness.length === 64 || witness.length === 65) return witness;
+}
+/**
+ * Converts a binary tree to a BIP371 type list.
+ * @param tree The binary tap tree.
+ * @param leaves A list of tapleaves. Optional.
+ * @param depth The current depth. Optional.
+ * @returns A list of BIP 371 tapleaves.
+ * @throws Throws an error if the taptree cannot be converted to a tapleaf list.
+ */
+function _tapTreeToList(tree, leaves = [], depth = 0) {
+ if (depth > MAX_TAPTREE_DEPTH) throw new Error('Max taptree depth exceeded.');
+ if (!tree) return [];
+ if (isTapleaf(tree)) {
+ leaves.push({
+ depth,
+ leafVersion: tree.version || LEAF_VERSION_TAPSCRIPT,
+ script: tree.output,
+ });
+ return leaves;
+ }
+ if (tree[0]) _tapTreeToList(tree[0], leaves, depth + 1);
+ if (tree[1]) _tapTreeToList(tree[1], leaves, depth + 1);
+ return leaves;
+}
+/**
+ * Inserts the tapleaves into the taproot tree.
+ * @param leaves The tapleaves to insert.
+ * @returns The taproot tree.
+ * @throws Throws an error if there is no room left to insert a tapleaf in the tree.
+ */
+function instertLeavesInTree(leaves) {
+ let tree;
+ for (const leaf of leaves) {
+ tree = instertLeafInTree(leaf, tree);
+ if (!tree) throw new Error(`No room left to insert tapleaf in tree`);
+ }
+ return tree;
+}
+/**
+ * Inserts a tapleaf into the taproot tree.
+ * @param leaf The tapleaf to insert.
+ * @param tree The taproot tree.
+ * @param depth The current depth. Optional.
+ * @returns The updated taproot tree.
+ */
+function instertLeafInTree(leaf, tree, depth = 0) {
+ if (depth > MAX_TAPTREE_DEPTH) throw new Error('Max taptree depth exceeded.');
+ if (leaf.depth === depth) {
+ if (!tree)
+ return {
+ output: leaf.script,
+ version: leaf.leafVersion,
+ };
+ return;
+ }
+ if (isTapleaf(tree)) return;
+ const leftSide = instertLeafInTree(leaf, tree && tree[0], depth + 1);
+ if (leftSide) return [leftSide, tree && tree[1]];
+ const rightSide = instertLeafInTree(leaf, tree && tree[1], depth + 1);
+ if (rightSide) return [tree && tree[0], rightSide];
+}
+/**
+ * Checks the input fields for mixed taproot and non-taproot fields.
+ * @param inputData The original input data.
+ * @param newInputData The new input data.
+ * @param action The action being performed.
+ * @throws Throws an error if the input fields are inconsistent.
+ */
+function checkMixedTaprootAndNonTaprootInputFields(
+ inputData,
+ newInputData,
+ action,
+) {
+ const isBadTaprootUpdate =
+ isTaprootInput(inputData) && hasNonTaprootFields(newInputData);
+ const isBadNonTaprootUpdate =
+ hasNonTaprootFields(inputData) && isTaprootInput(newInputData);
+ const hasMixedFields =
+ inputData === newInputData &&
+ isTaprootInput(newInputData) &&
+ hasNonTaprootFields(newInputData); // todo: bad? use !===
+ if (isBadTaprootUpdate || isBadNonTaprootUpdate || hasMixedFields)
+ throw new Error(
+ `Invalid arguments for Psbt.${action}. ` +
+ `Cannot use both taproot and non-taproot fields.`,
+ );
+}
+/**
+ * Checks the output fields for mixed taproot and non-taproot fields.
+ * @param inputData The original output data.
+ * @param newInputData The new output data.
+ * @param action The action being performed.
+ * @throws Throws an error if the output fields are inconsistent.
+ */
+function checkMixedTaprootAndNonTaprootOutputFields(
+ inputData,
+ newInputData,
+ action,
+) {
+ const isBadTaprootUpdate =
+ isTaprootOutput(inputData) && hasNonTaprootFields(newInputData);
+ const isBadNonTaprootUpdate =
+ hasNonTaprootFields(inputData) && isTaprootOutput(newInputData);
+ const hasMixedFields =
+ inputData === newInputData &&
+ isTaprootOutput(newInputData) &&
+ hasNonTaprootFields(newInputData);
+ if (isBadTaprootUpdate || isBadNonTaprootUpdate || hasMixedFields)
+ throw new Error(
+ `Invalid arguments for Psbt.${action}. ` +
+ `Cannot use both taproot and non-taproot fields.`,
+ );
+}
+/**
+ * Checks if the tap leaf is part of the tap tree for the given input data.
+ * Throws an error if the tap leaf is not part of the tap tree.
+ * @param inputData - The original PsbtInput data.
+ * @param newInputData - The new PsbtInput data.
+ * @param action - The action being performed.
+ * @throws {Error} - If the tap leaf is not part of the tap tree.
+ */
+function checkIfTapLeafInTree(inputData, newInputData, action) {
+ if (newInputData.tapMerkleRoot) {
+ const newLeafsInTree = (newInputData.tapLeafScript || []).every(l =>
+ isTapLeafInTree(l, newInputData.tapMerkleRoot),
+ );
+ const oldLeafsInTree = (inputData.tapLeafScript || []).every(l =>
+ isTapLeafInTree(l, newInputData.tapMerkleRoot),
+ );
+ if (!newLeafsInTree || !oldLeafsInTree)
+ throw new Error(
+ `Invalid arguments for Psbt.${action}. Tapleaf not part of taptree.`,
+ );
+ } else if (inputData.tapMerkleRoot) {
+ const newLeafsInTree = (newInputData.tapLeafScript || []).every(l =>
+ isTapLeafInTree(l, inputData.tapMerkleRoot),
+ );
+ if (!newLeafsInTree)
+ throw new Error(
+ `Invalid arguments for Psbt.${action}. Tapleaf not part of taptree.`,
+ );
+ }
+}
+/**
+ * Checks if a TapLeafScript is present in a Merkle tree.
+ * @param tapLeaf The TapLeafScript to check.
+ * @param merkleRoot The Merkle root of the tree. If not provided, the function assumes the TapLeafScript is present.
+ * @returns A boolean indicating whether the TapLeafScript is present in the tree.
+ */
+function isTapLeafInTree(tapLeaf, merkleRoot) {
+ if (!merkleRoot) return true;
+ const leafHash = tapleafHash({
+ output: tapLeaf.script,
+ version: tapLeaf.leafVersion,
+ });
+ const rootHash = rootHashFromPath(tapLeaf.controlBlock, leafHash);
+ return tools.compare(rootHash, merkleRoot) === 0;
+}
+/**
+ * Sorts the signatures in the input's tapScriptSig array based on their position in the tapLeaf script.
+ *
+ * @param input - The PsbtInput object.
+ * @param tapLeaf - The TapLeafScript object.
+ * @returns An array of sorted signatures as Buffers.
+ */
+function sortSignatures(input, tapLeaf) {
+ const leafHash = tapleafHash({
+ output: tapLeaf.script,
+ version: tapLeaf.leafVersion,
+ });
+ return (
+ (input.tapScriptSig || [])
+ // .filter(tss => tss.leafHash.equals(leafHash))
+ .filter(tss => tools.compare(tss.leafHash, leafHash) === 0)
+ .map(tss => addPubkeyPositionInScript(tapLeaf.script, tss))
+ .sort((t1, t2) => t2.positionInScript - t1.positionInScript)
+ .map(t => t.signature)
+ );
+}
+/**
+ * Adds the position of a public key in a script to a TapScriptSig object.
+ * @param script The script in which to find the position of the public key.
+ * @param tss The TapScriptSig object to add the position to.
+ * @returns A TapScriptSigWitPosition object with the added position.
+ */
+function addPubkeyPositionInScript(script, tss) {
+ return Object.assign(
+ {
+ positionInScript: pubkeyPositionInScript(tss.pubkey, script),
+ },
+ tss,
+ );
+}
+/**
+ * Find tapleaf by hash, or get the signed tapleaf with the shortest path.
+ */
+function findTapLeafToFinalize(input, inputIndex, leafHashToFinalize) {
+ if (!input.tapScriptSig || !input.tapScriptSig.length)
+ throw new Error(
+ `Can not finalize taproot input #${inputIndex}. No tapleaf script signature provided.`,
+ );
+ const tapLeaf = (input.tapLeafScript || [])
+ .sort((a, b) => a.controlBlock.length - b.controlBlock.length)
+ .find(leaf =>
+ canFinalizeLeaf(leaf, input.tapScriptSig, leafHashToFinalize),
+ );
+ if (!tapLeaf)
+ throw new Error(
+ `Can not finalize taproot input #${inputIndex}. Signature for tapleaf script not found.`,
+ );
+ return tapLeaf;
+}
+/**
+ * Determines whether a TapLeafScript can be finalized.
+ *
+ * @param leaf - The TapLeafScript to check.
+ * @param tapScriptSig - The array of TapScriptSig objects.
+ * @param hash - The optional hash to compare with the leaf hash.
+ * @returns A boolean indicating whether the TapLeafScript can be finalized.
+ */
+function canFinalizeLeaf(leaf, tapScriptSig, hash) {
+ const leafHash = tapleafHash({
+ output: leaf.script,
+ version: leaf.leafVersion,
+ });
+ const whiteListedHash = !hash || tools.compare(leafHash, hash) === 0;
+ return (
+ whiteListedHash &&
+ tapScriptSig.find(tss => tools.compare(tss.leafHash, leafHash) === 0) !==
+ undefined
+ );
+}
+/**
+ * Checks if the given PsbtInput or PsbtOutput has non-taproot fields.
+ * Non-taproot fields include redeemScript, witnessScript, and bip32Derivation.
+ * @param io The PsbtInput or PsbtOutput to check.
+ * @returns A boolean indicating whether the given input or output has non-taproot fields.
+ */
+function hasNonTaprootFields(io) {
+ return (
+ io &&
+ !!(
+ io.redeemScript ||
+ io.witnessScript ||
+ (io.bip32Derivation && io.bip32Derivation.length)
+ )
+ );
+}
diff --git a/src/esm/psbt/psbtutils.js b/src/esm/psbt/psbtutils.js
new file mode 100644
index 000000000..fa4bc2758
--- /dev/null
+++ b/src/esm/psbt/psbtutils.js
@@ -0,0 +1,168 @@
+import * as varuint from 'varuint-bitcoin';
+import * as bscript from '../script.js';
+import { Transaction } from '../transaction.js';
+import { hash160 } from '../crypto.js';
+import * as payments from '../payments/index.js';
+import * as tools from 'uint8array-tools';
+/**
+ * Checks if a given payment factory can generate a payment script from a given script.
+ * @param payment The payment factory to check.
+ * @returns A function that takes a script and returns a boolean indicating whether the payment factory can generate a payment script from the script.
+ */
+function isPaymentFactory(payment) {
+ return script => {
+ try {
+ payment({ output: script });
+ return true;
+ } catch (err) {
+ return false;
+ }
+ };
+}
+export const isP2MS = isPaymentFactory(payments.p2ms);
+export const isP2PK = isPaymentFactory(payments.p2pk);
+export const isP2PKH = isPaymentFactory(payments.p2pkh);
+export const isP2WPKH = isPaymentFactory(payments.p2wpkh);
+export const isP2WSHScript = isPaymentFactory(payments.p2wsh);
+export const isP2SHScript = isPaymentFactory(payments.p2sh);
+export const isP2TR = isPaymentFactory(payments.p2tr);
+/**
+ * Converts a witness stack to a script witness.
+ * @param witness The witness stack to convert.
+ * @returns The script witness as a Buffer.
+ */
+export function witnessStackToScriptWitness(witness) {
+ let buffer = new Uint8Array(0);
+ function writeSlice(slice) {
+ buffer = tools.concat([buffer, slice]);
+ }
+ function writeVarInt(i) {
+ const currentLen = buffer.length;
+ const varintLen = varuint.encodingLength(i);
+ buffer = tools.concat([buffer, new Uint8Array(varintLen)]);
+ varuint.encode(i, buffer, currentLen);
+ }
+ function writeVarSlice(slice) {
+ writeVarInt(slice.length);
+ writeSlice(slice);
+ }
+ function writeVector(vector) {
+ writeVarInt(vector.length);
+ vector.forEach(writeVarSlice);
+ }
+ writeVector(witness);
+ return buffer;
+}
+/**
+ * Finds the position of a public key in a script.
+ * @param pubkey The public key to search for.
+ * @param script The script to search in.
+ * @returns The index of the public key in the script, or -1 if not found.
+ * @throws {Error} If there is an unknown script error.
+ */
+export function pubkeyPositionInScript(pubkey, script) {
+ const pubkeyHash = hash160(pubkey);
+ const pubkeyXOnly = pubkey.slice(1, 33); // slice before calling?
+ const decompiled = bscript.decompile(script);
+ if (decompiled === null) throw new Error('Unknown script error');
+ return decompiled.findIndex(element => {
+ if (typeof element === 'number') return false;
+ return (
+ tools.compare(pubkey, element) === 0 ||
+ tools.compare(pubkeyHash, element) === 0 ||
+ tools.compare(pubkeyXOnly, element) === 0
+ );
+ });
+}
+/**
+ * Checks if a public key is present in a script.
+ * @param pubkey The public key to check.
+ * @param script The script to search in.
+ * @returns A boolean indicating whether the public key is present in the script.
+ */
+export function pubkeyInScript(pubkey, script) {
+ return pubkeyPositionInScript(pubkey, script) !== -1;
+}
+/**
+ * Checks if an input contains a signature for a specific action.
+ * @param input - The input to check.
+ * @param action - The action to check for.
+ * @returns A boolean indicating whether the input contains a signature for the specified action.
+ */
+export function checkInputForSig(input, action) {
+ const pSigs = extractPartialSigs(input);
+ return pSigs.some(pSig =>
+ signatureBlocksAction(pSig, bscript.signature.decode, action),
+ );
+}
+/**
+ * Determines if a given action is allowed for a signature block.
+ * @param signature - The signature block.
+ * @param signatureDecodeFn - The function used to decode the signature.
+ * @param action - The action to be checked.
+ * @returns True if the action is allowed, false otherwise.
+ */
+export function signatureBlocksAction(signature, signatureDecodeFn, action) {
+ const { hashType } = signatureDecodeFn(signature);
+ const whitelist = [];
+ const isAnyoneCanPay = hashType & Transaction.SIGHASH_ANYONECANPAY;
+ if (isAnyoneCanPay) whitelist.push('addInput');
+ const hashMod = hashType & 0x1f;
+ switch (hashMod) {
+ case Transaction.SIGHASH_ALL:
+ break;
+ case Transaction.SIGHASH_SINGLE:
+ case Transaction.SIGHASH_NONE:
+ whitelist.push('addOutput');
+ whitelist.push('setInputSequence');
+ break;
+ }
+ if (whitelist.indexOf(action) === -1) {
+ return true;
+ }
+ return false;
+}
+/**
+ * Extracts the signatures from a PsbtInput object.
+ * If the input has partial signatures, it returns an array of the signatures.
+ * If the input does not have partial signatures, it checks if it has a finalScriptSig or finalScriptWitness.
+ * If it does, it extracts the signatures from the final scripts and returns them.
+ * If none of the above conditions are met, it returns an empty array.
+ *
+ * @param input - The PsbtInput object from which to extract the signatures.
+ * @returns An array of signatures extracted from the PsbtInput object.
+ */
+function extractPartialSigs(input) {
+ let pSigs = [];
+ if ((input.partialSig || []).length === 0) {
+ if (!input.finalScriptSig && !input.finalScriptWitness) return [];
+ pSigs = getPsigsFromInputFinalScripts(input);
+ } else {
+ pSigs = input.partialSig;
+ }
+ return pSigs.map(p => p.signature);
+}
+/**
+ * Retrieves the partial signatures (Psigs) from the input's final scripts.
+ * Psigs are extracted from both the final scriptSig and final scriptWitness of the input.
+ * Only canonical script signatures are considered.
+ *
+ * @param input - The PsbtInput object representing the input.
+ * @returns An array of PartialSig objects containing the extracted Psigs.
+ */
+function getPsigsFromInputFinalScripts(input) {
+ const scriptItems = !input.finalScriptSig
+ ? []
+ : bscript.decompile(input.finalScriptSig) || [];
+ const witnessItems = !input.finalScriptWitness
+ ? []
+ : bscript.decompile(input.finalScriptWitness) || [];
+ return scriptItems
+ .concat(witnessItems)
+ .filter(item => {
+ return (
+ item instanceof Uint8Array && bscript.isCanonicalScriptSignature(item)
+ );
+ })
+ .map(sig => ({ signature: sig }));
+}
diff --git a/src/esm/push_data.js b/src/esm/push_data.js
new file mode 100644
index 000000000..12b233a27
--- /dev/null
+++ b/src/esm/push_data.js
@@ -0,0 +1,77 @@
+import { OPS } from './ops.js';
+import * as tools from 'uint8array-tools';
+/**
+ * Calculates the encoding length of a number used for push data in Bitcoin transactions.
+ * @param i The number to calculate the encoding length for.
+ * @returns The encoding length of the number.
+ */
+export function encodingLength(i) {
+ return i < OPS.OP_PUSHDATA1 ? 1 : i <= 0xff ? 2 : i <= 0xffff ? 3 : 5;
+}
+/**
+ * Encodes a number into a buffer using a variable-length encoding scheme.
+ * The encoded buffer is written starting at the specified offset.
+ * Returns the size of the encoded buffer.
+ *
+ * @param buffer - The buffer to write the encoded data into.
+ * @param num - The number to encode.
+ * @param offset - The offset at which to start writing the encoded buffer.
+ * @returns The size of the encoded buffer.
+ */
+export function encode(buffer, num, offset) {
+ const size = encodingLength(num);
+ // ~6 bit
+ if (size === 1) {
+ tools.writeUInt8(buffer, offset, num);
+ // 8 bit
+ } else if (size === 2) {
+ tools.writeUInt8(buffer, offset, OPS.OP_PUSHDATA1);
+ tools.writeUInt8(buffer, offset + 1, num);
+ // 16 bit
+ } else if (size === 3) {
+ tools.writeUInt8(buffer, offset, OPS.OP_PUSHDATA2);
+ tools.writeUInt16(buffer, offset + 1, num, 'LE');
+ // 32 bit
+ } else {
+ tools.writeUInt8(buffer, offset, OPS.OP_PUSHDATA4);
+ tools.writeUInt32(buffer, offset + 1, num, 'LE');
+ }
+ return size;
+}
+/**
+ * Decodes a buffer and returns information about the opcode, number, and size.
+ * @param buffer - The buffer to decode.
+ * @param offset - The offset within the buffer to start decoding.
+ * @returns An object containing the opcode, number, and size, or null if decoding fails.
+ */
+export function decode(buffer, offset) {
+ const opcode = tools.readUInt8(buffer, offset);
+ let num;
+ let size;
+ // ~6 bit
+ if (opcode < OPS.OP_PUSHDATA1) {
+ num = opcode;
+ size = 1;
+ // 8 bit
+ } else if (opcode === OPS.OP_PUSHDATA1) {
+ if (offset + 2 > buffer.length) return null;
+ num = tools.readUInt8(buffer, offset + 1);
+ size = 2;
+ // 16 bit
+ } else if (opcode === OPS.OP_PUSHDATA2) {
+ if (offset + 3 > buffer.length) return null;
+ num = tools.readUInt16(buffer, offset + 1, 'LE');
+ size = 3;
+ // 32 bit
+ } else {
+ if (offset + 5 > buffer.length) return null;
+ if (opcode !== OPS.OP_PUSHDATA4) throw new Error('Unexpected opcode');
+ num = tools.readUInt32(buffer, offset + 1, 'LE');
+ size = 5;
+ }
+ return {
+ opcode,
+ number: num,
+ size,
+ };
+}
diff --git a/src/esm/script.js b/src/esm/script.js
new file mode 100644
index 000000000..4f566e182
--- /dev/null
+++ b/src/esm/script.js
@@ -0,0 +1,277 @@
+/**
+ * Script tools module for working with Bitcoin scripts.
+ * Provides utilities such as decompiling, compiling, converting to/from ASM, stack manipulation,
+ * and script validation functions.
+ *
+ * @packageDocumentation
+ */
+import * as bip66 from './bip66.js';
+import { OPS } from './ops.js';
+import * as pushdata from './push_data.js';
+import * as scriptNumber from './script_number.js';
+import * as scriptSignature from './script_signature.js';
+import * as types from './types.js';
+import * as tools from 'uint8array-tools';
+import * as v from 'valibot';
+/** Base opcode for OP_INT values. */
+const OP_INT_BASE = OPS.OP_RESERVED; // OP_1 - 1
+export { OPS };
+/** Validation schema for a Bitcoin script stack. */
+const StackSchema = v.array(v.union([v.instance(Uint8Array), v.number()]));
+/**
+ * Determines if a value corresponds to an OP_INT opcode.
+ *
+ * @param value - The opcode to check.
+ * @returns True if the value is an OP_INT, false otherwise.
+ */
+function isOPInt(value) {
+ return (
+ v.is(v.number(), value) &&
+ (value === OPS.OP_0 ||
+ (value >= OPS.OP_1 && value <= OPS.OP_16) ||
+ value === OPS.OP_1NEGATE)
+ );
+}
+/**
+ * Checks if a script chunk is push-only (contains only data or OP_INT opcodes).
+ *
+ * @param value - The chunk to check.
+ * @returns True if the chunk is push-only, false otherwise.
+ */
+function isPushOnlyChunk(value) {
+ return v.is(types.BufferSchema, value) || isOPInt(value);
+}
+/**
+ * Determines if a stack consists of only push operations.
+ *
+ * @param value - The stack to check.
+ * @returns True if all elements in the stack are push-only, false otherwise.
+ */
+export function isPushOnly(value) {
+ return v.is(v.pipe(v.any(), v.everyItem(isPushOnlyChunk)), value);
+}
+/**
+ * Counts the number of non-push-only opcodes in a stack.
+ *
+ * @param value - The stack to analyze.
+ * @returns The count of non-push-only opcodes.
+ */
+export function countNonPushOnlyOPs(value) {
+ return value.length - value.filter(isPushOnlyChunk).length;
+}
+/**
+ * Converts a minimal script buffer to its corresponding opcode, if applicable.
+ *
+ * @param buffer - The buffer to check.
+ * @returns The corresponding opcode or undefined if not minimal.
+ */
+function asMinimalOP(buffer) {
+ if (buffer.length === 0) return OPS.OP_0;
+ if (buffer.length !== 1) return;
+ if (buffer[0] >= 1 && buffer[0] <= 16) return OP_INT_BASE + buffer[0];
+ if (buffer[0] === 0x81) return OPS.OP_1NEGATE;
+}
+/**
+ * Determines if a buffer or stack is a Uint8Array.
+ *
+ * @param buf - The buffer or stack to check.
+ * @returns True if the input is a Uint8Array, false otherwise.
+ */
+function chunksIsBuffer(buf) {
+ return buf instanceof Uint8Array;
+}
+/**
+ * Determines if a buffer or stack is a valid stack.
+ *
+ * @param buf - The buffer or stack to check.
+ * @returns True if the input is a stack, false otherwise.
+ */
+function chunksIsArray(buf) {
+ return v.is(StackSchema, buf);
+}
+/**
+ * Determines if a single chunk is a Uint8Array.
+ *
+ * @param buf - The chunk to check.
+ * @returns True if the chunk is a Uint8Array, false otherwise.
+ */
+function singleChunkIsBuffer(buf) {
+ return buf instanceof Uint8Array;
+}
+/**
+ * Compiles an array of script chunks into a Uint8Array.
+ *
+ * @param chunks - The chunks to compile.
+ * @returns The compiled script as a Uint8Array.
+ * @throws Error if compilation fails.
+ */
+export function compile(chunks) {
+ if (chunksIsBuffer(chunks)) return chunks;
+ v.parse(StackSchema, chunks);
+ const bufferSize = chunks.reduce((accum, chunk) => {
+ if (singleChunkIsBuffer(chunk)) {
+ // adhere to BIP62.3, minimal push policy
+ if (chunk.length === 1 && asMinimalOP(chunk) !== undefined) {
+ return accum + 1;
+ }
+ return accum + pushdata.encodingLength(chunk.length) + chunk.length;
+ }
+ return accum + 1;
+ }, 0);
+ const buffer = new Uint8Array(bufferSize);
+ let offset = 0;
+ chunks.forEach(chunk => {
+ if (singleChunkIsBuffer(chunk)) {
+ // adhere to BIP62.3, minimal push policy
+ const opcode = asMinimalOP(chunk);
+ if (opcode !== undefined) {
+ tools.writeUInt8(buffer, offset, opcode);
+ offset += 1;
+ return;
+ }
+ offset += pushdata.encode(buffer, chunk.length, offset);
+ buffer.set(chunk, offset);
+ offset += chunk.length;
+ // opcode
+ } else {
+ tools.writeUInt8(buffer, offset, chunk);
+ offset += 1;
+ }
+ });
+ if (offset !== buffer.length) throw new Error('Could not decode chunks');
+ return buffer;
+}
+/**
+ * Decompiles a script buffer into an array of chunks.
+ *
+ * @param buffer - The script buffer to decompile.
+ * @returns The decompiled chunks or null if decompilation fails.
+ */
+export function decompile(buffer) {
+ if (chunksIsArray(buffer)) return buffer;
+ v.parse(types.BufferSchema, buffer);
+ const chunks = [];
+ let i = 0;
+ while (i < buffer.length) {
+ const opcode = buffer[i];
+ if (opcode > OPS.OP_0 && opcode <= OPS.OP_PUSHDATA4) {
+ const d = pushdata.decode(buffer, i);
+ // did reading a pushDataInt fail?
+ if (d === null) return null;
+ i += d.size;
+ // attempt to read too much data?
+ if (i + d.number > buffer.length) return null;
+ const data = buffer.slice(i, i + d.number);
+ i += d.number;
+ // decompile minimally
+ const op = asMinimalOP(data);
+ if (op !== undefined) {
+ chunks.push(op);
+ } else {
+ chunks.push(data);
+ }
+ } else {
+ chunks.push(opcode);
+ i += 1;
+ }
+ }
+ return chunks;
+}
+/**
+ * Converts the given chunks into an ASM (Assembly) string representation.
+ * If the chunks parameter is a Buffer, it will be decompiled into a Stack before conversion.
+ * @param chunks - The chunks to convert into ASM.
+ * @returns The ASM string representation of the chunks.
+ */
+export function toASM(chunks) {
+ if (chunksIsBuffer(chunks)) {
+ chunks = decompile(chunks);
+ }
+ if (!chunks) {
+ throw new Error('Could not convert invalid chunks to ASM');
+ }
+ return chunks
+ .map(chunk => {
+ if (singleChunkIsBuffer(chunk)) {
+ const op = asMinimalOP(chunk);
+ if (op === undefined) return tools.toHex(chunk);
+ chunk = op;
+ }
+ // opcode!
+ return OPS[chunk];
+ })
+ .join(' ');
+}
+/**
+ * Converts an ASM string to a Buffer.
+ * @param asm The ASM string to convert.
+ * @returns The converted Buffer.
+ */
+export function fromASM(asm) {
+ v.parse(v.string(), asm);
+ // Compile the ASM string into a Uint8Array
+ return compile(
+ asm.split(' ').map(chunk => {
+ // Check if the chunk is an opcode
+ if (isNaN(Number(chunk)) && chunk in OPS) {
+ return OPS[chunk];
+ }
+ // Validate if the chunk is a hexadecimal string
+ v.parse(types.HexSchema, chunk);
+ // Convert the chunk to Uint8Array data
+ return tools.fromHex(chunk);
+ }),
+ );
+}
+/**
+ * Converts the given chunks into a stack of buffers.
+ *
+ * @param chunks - The chunks to convert.
+ * @returns The stack of buffers.
+ */
+export function toStack(chunks) {
+ chunks = decompile(chunks);
+ v.parse(v.custom(isPushOnly), chunks);
+ return chunks.map(op => {
+ if (singleChunkIsBuffer(op)) return op;
+ if (op === OPS.OP_0) return new Uint8Array(0);
+ return scriptNumber.encode(op - OP_INT_BASE);
+ });
+}
+/**
+ * Checks if the provided buffer is a canonical public key.
+ *
+ * @param buffer - The buffer to check, expected to be a Uint8Array.
+ * @returns A boolean indicating whether the buffer is a canonical public key.
+ */
+export function isCanonicalPubKey(buffer) {
+ return types.isPoint(buffer);
+}
+/**
+ * Checks if the provided hash type is defined.
+ *
+ * A hash type is considered defined if its modified value (after masking with ~0x80)
+ * is greater than 0x00 and less than 0x04.
+ *
+ * @param hashType - The hash type to check.
+ * @returns True if the hash type is defined, false otherwise.
+ */
+export function isDefinedHashType(hashType) {
+ const hashTypeMod = hashType & ~0x80;
+ return hashTypeMod > 0x00 && hashTypeMod < 0x04;
+}
+/**
+ * Checks if the provided buffer is a canonical script signature.
+ *
+ * A canonical script signature is a valid DER-encoded signature followed by a valid hash type byte.
+ *
+ * @param buffer - The buffer to check.
+ * @returns `true` if the buffer is a canonical script signature, `false` otherwise.
+ */
+export function isCanonicalScriptSignature(buffer) {
+ if (!(buffer instanceof Uint8Array)) return false;
+ if (!isDefinedHashType(buffer[buffer.length - 1])) return false;
+ return bip66.check(buffer.slice(0, -1));
+}
+export const number = scriptNumber;
+export const signature = scriptSignature;
diff --git a/src/esm/script_number.js b/src/esm/script_number.js
new file mode 100644
index 000000000..b96c8696a
--- /dev/null
+++ b/src/esm/script_number.js
@@ -0,0 +1,74 @@
+import * as tools from 'uint8array-tools';
+/**
+ * Decodes a script number from a buffer.
+ *
+ * @param buffer - The buffer containing the script number.
+ * @param maxLength - The maximum length of the script number. Defaults to 4.
+ * @param minimal - Whether the script number should be minimal. Defaults to true.
+ * @returns The decoded script number.
+ * @throws {TypeError} If the script number overflows the maximum length.
+ * @throws {Error} If the script number is not minimally encoded when minimal is true.
+ */
+export function decode(buffer, maxLength, minimal) {
+ maxLength = maxLength || 4;
+ minimal = minimal === undefined ? true : minimal;
+ const length = buffer.length;
+ if (length === 0) return 0;
+ if (length > maxLength) throw new TypeError('Script number overflow');
+ if (minimal) {
+ if ((buffer[length - 1] & 0x7f) === 0) {
+ if (length <= 1 || (buffer[length - 2] & 0x80) === 0)
+ throw new Error('Non-minimally encoded script number');
+ }
+ }
+ // 40-bit
+ if (length === 5) {
+ const a = tools.readUInt32(buffer, 0, 'LE');
+ const b = tools.readUInt8(buffer, 4);
+ if (b & 0x80) return -((b & ~0x80) * 0x100000000 + a);
+ return b * 0x100000000 + a;
+ }
+ // 32-bit / 24-bit / 16-bit / 8-bit
+ let result = 0;
+ for (let i = 0; i < length; ++i) {
+ result |= buffer[i] << (8 * i);
+ }
+ if (buffer[length - 1] & 0x80)
+ return -(result & ~(0x80 << (8 * (length - 1))));
+ return result;
+}
+function scriptNumSize(i) {
+ return i > 0x7fffffff
+ ? 5
+ : i > 0x7fffff
+ ? 4
+ : i > 0x7fff
+ ? 3
+ : i > 0x7f
+ ? 2
+ : i > 0x00
+ ? 1
+ : 0;
+}
+/**
+ * Encodes a number into a Uint8Array using a specific format.
+ *
+ * @param _number - The number to encode.
+ * @returns The encoded number as a Uint8Array.
+ */
+export function encode(_number) {
+ let value = Math.abs(_number);
+ const size = scriptNumSize(value);
+ const buffer = new Uint8Array(size);
+ const negative = _number < 0;
+ for (let i = 0; i < size; ++i) {
+ tools.writeUInt8(buffer, i, value & 0xff);
+ value >>= 8;
+ }
+ if (buffer[size - 1] & 0x80) {
+ tools.writeUInt8(buffer, size - 1, negative ? 0x80 : 0x00);
+ } else if (negative) {
+ buffer[size - 1] |= 0x80;
+ }
+ return buffer;
+}
diff --git a/src/esm/script_signature.js b/src/esm/script_signature.js
new file mode 100644
index 000000000..fbf3f95d9
--- /dev/null
+++ b/src/esm/script_signature.js
@@ -0,0 +1,75 @@
+import * as bip66 from './bip66.js';
+import { isDefinedHashType } from './script.js';
+import * as v from 'valibot';
+import * as tools from 'uint8array-tools';
+import { NBufferSchemaFactory, UInt8Schema } from './types.js';
+const ZERO = new Uint8Array(1);
+/**
+ * Converts a buffer to a DER-encoded buffer.
+ * @param x - The buffer to be converted.
+ * @returns The DER-encoded buffer.
+ */
+function toDER(x) {
+ let i = 0;
+ while (x[i] === 0) ++i;
+ if (i === x.length) return ZERO;
+ x = x.slice(i);
+ if (x[0] & 0x80) return tools.concat([ZERO, x]);
+ return x;
+}
+/**
+ * Converts a DER-encoded signature to a buffer.
+ * If the first byte of the input buffer is 0x00, it is skipped.
+ * The resulting buffer is 32 bytes long, filled with zeros if necessary.
+ * @param x - The DER-encoded signature.
+ * @returns The converted buffer.
+ */
+function fromDER(x) {
+ if (x[0] === 0x00) x = x.slice(1);
+ const buffer = new Uint8Array(32);
+ const bstart = Math.max(0, 32 - x.length);
+ buffer.set(x, bstart);
+ return buffer;
+}
+// BIP62: 1 byte hashType flag (only 0x01, 0x02, 0x03, 0x81, 0x82 and 0x83 are allowed)
+/**
+ * Decodes a buffer into a ScriptSignature object.
+ * @param buffer - The buffer to decode.
+ * @returns The decoded ScriptSignature object.
+ * @throws Error if the hashType is invalid.
+ */
+export function decode(buffer) {
+ const hashType = tools.readUInt8(buffer, buffer.length - 1);
+ if (!isDefinedHashType(hashType)) {
+ throw new Error('Invalid hashType ' + hashType);
+ }
+ const decoded = bip66.decode(buffer.subarray(0, -1));
+ const r = fromDER(decoded.r);
+ const s = fromDER(decoded.s);
+ const signature = tools.concat([r, s]);
+ return { signature, hashType };
+}
+/**
+ * Encodes a signature and hash type into a buffer.
+ * @param signature - The signature to encode.
+ * @param hashType - The hash type to encode.
+ * @returns The encoded buffer.
+ * @throws Error if the hashType is invalid.
+ */
+export function encode(signature, hashType) {
+ v.parse(
+ v.object({
+ signature: NBufferSchemaFactory(64),
+ hashType: UInt8Schema,
+ }),
+ { signature, hashType },
+ );
+ if (!isDefinedHashType(hashType)) {
+ throw new Error('Invalid hashType ' + hashType);
+ }
+ const hashTypeBuffer = new Uint8Array(1);
+ tools.writeUInt8(hashTypeBuffer, 0, hashType);
+ const r = toDER(signature.slice(0, 32));
+ const s = toDER(signature.slice(32, 64));
+ return tools.concat([bip66.encode(r, s), hashTypeBuffer]);
+}
diff --git a/src/transaction.js b/src/esm/transaction.js
similarity index 63%
rename from src/transaction.js
rename to src/esm/transaction.js
index 6f1382cfa..54e9ecc00 100644
--- a/src/transaction.js
+++ b/src/esm/transaction.js
@@ -1,36 +1,38 @@
-'use strict';
-Object.defineProperty(exports, '__esModule', { value: true });
-exports.Transaction = void 0;
-const bufferutils_1 = require('./bufferutils');
-const bcrypto = require('./crypto');
-const bscript = require('./script');
-const script_1 = require('./script');
-const types = require('./types');
-const { typeforce } = types;
+import {
+ BufferReader,
+ BufferWriter,
+ reverseBuffer,
+ varuint,
+} from './bufferutils.js';
+import * as bcrypto from './crypto.js';
+import { sha256 } from '@noble/hashes/sha256';
+import * as bscript from './script.js';
+import { OPS as opcodes } from './script.js';
+import * as types from './types.js';
+import * as tools from 'uint8array-tools';
+import * as v from 'valibot';
function varSliceSize(someScript) {
const length = someScript.length;
- return bufferutils_1.varuint.encodingLength(length) + length;
+ return varuint.encodingLength(length) + length;
}
function vectorSize(someVector) {
const length = someVector.length;
return (
- bufferutils_1.varuint.encodingLength(length) +
+ varuint.encodingLength(length) +
someVector.reduce((sum, witness) => {
return sum + varSliceSize(witness);
}, 0)
);
}
-const EMPTY_BUFFER = Buffer.allocUnsafe(0);
+const EMPTY_BUFFER = new Uint8Array(0);
const EMPTY_WITNESS = [];
-const ZERO = Buffer.from(
+const ZERO = tools.fromHex(
'0000000000000000000000000000000000000000000000000000000000000000',
- 'hex',
);
-const ONE = Buffer.from(
+const ONE = tools.fromHex(
'0000000000000000000000000000000000000000000000000000000000000001',
- 'hex',
);
-const VALUE_UINT64_MAX = Buffer.from('ffffffffffffffff', 'hex');
+const VALUE_UINT64_MAX = tools.fromHex('ffffffffffffffff');
const BLANK_OUTPUT = {
script: EMPTY_BUFFER,
valueBuffer: VALUE_UINT64_MAX,
@@ -38,25 +40,39 @@ const BLANK_OUTPUT = {
function isOutput(out) {
return out.value !== undefined;
}
-class Transaction {
- constructor() {
- this.version = 1;
- this.locktime = 0;
- this.ins = [];
- this.outs = [];
- }
+/**
+ * Represents a Bitcoin transaction.
+ */
+export class Transaction {
+ static DEFAULT_SEQUENCE = 0xffffffff;
+ static SIGHASH_DEFAULT = 0x00;
+ static SIGHASH_ALL = 0x01;
+ static SIGHASH_NONE = 0x02;
+ static SIGHASH_SINGLE = 0x03;
+ static SIGHASH_ANYONECANPAY = 0x80;
+ static SIGHASH_OUTPUT_MASK = 0x03;
+ static SIGHASH_INPUT_MASK = 0x80;
+ static ADVANCED_TRANSACTION_MARKER = 0x00;
+ static ADVANCED_TRANSACTION_FLAG = 0x01;
+ static MWEB_PEGOUT_FLAG = 0x08;
static fromBuffer(buffer, _NO_STRICT) {
- const bufferReader = new bufferutils_1.BufferReader(buffer);
+ const bufferReader = new BufferReader(buffer);
const tx = new Transaction();
- tx.version = bufferReader.readInt32();
+ tx.version = bufferReader.readUInt32();
const marker = bufferReader.readUInt8();
const flag = bufferReader.readUInt8();
let hasWitnesses = false;
+ let hasMweb = false;
if (
marker === Transaction.ADVANCED_TRANSACTION_MARKER &&
flag === Transaction.ADVANCED_TRANSACTION_FLAG
) {
hasWitnesses = true;
+ } else if (
+ marker === Transaction.ADVANCED_TRANSACTION_MARKER &&
+ flag === Transaction.MWEB_PEGOUT_FLAG
+ ) {
+ hasMweb = true;
} else {
bufferReader.offset -= 2;
}
@@ -73,7 +89,7 @@ class Transaction {
const voutLen = bufferReader.readVarInt();
for (let i = 0; i < voutLen; ++i) {
tx.outs.push({
- value: bufferReader.readUInt64(),
+ value: bufferReader.readInt64(),
script: bufferReader.readVarSlice(),
});
}
@@ -85,6 +101,10 @@ class Transaction {
if (!tx.hasWitnesses())
throw new Error('Transaction has superfluous witness data');
}
+ // MWEB peg-out transactions have an extra 0x00 byte before locktime
+ if (hasMweb) {
+ bufferReader.readUInt8();
+ }
tx.locktime = bufferReader.readUInt32();
if (_NO_STRICT) return tx;
if (bufferReader.offset !== buffer.length)
@@ -92,31 +112,35 @@ class Transaction {
return tx;
}
static fromHex(hex) {
- return Transaction.fromBuffer(Buffer.from(hex, 'hex'), false);
+ return Transaction.fromBuffer(tools.fromHex(hex), false);
}
static isCoinbaseHash(buffer) {
- typeforce(types.Hash256bit, buffer);
+ v.parse(types.Hash256bitSchema, buffer);
for (let i = 0; i < 32; ++i) {
if (buffer[i] !== 0) return false;
}
return true;
}
+ version = 1;
+ locktime = 0;
+ ins = [];
+ outs = [];
isCoinbase() {
return (
this.ins.length === 1 && Transaction.isCoinbaseHash(this.ins[0].hash)
);
}
addInput(hash, index, sequence, scriptSig) {
- typeforce(
- types.tuple(
- types.Hash256bit,
- types.UInt32,
- types.maybe(types.UInt32),
- types.maybe(types.Buffer),
- ),
- arguments,
+ v.parse(
+ v.tuple([
+ types.Hash256bitSchema,
+ types.UInt32Schema,
+ v.nullable(v.optional(types.UInt32Schema)),
+ v.nullable(v.optional(types.BufferSchema)),
+ ]),
+ [hash, index, sequence, scriptSig],
);
- if (types.Null(sequence)) {
+ if (sequence === undefined || sequence === null) {
sequence = Transaction.DEFAULT_SEQUENCE;
}
// Add the input and return the input's index
@@ -131,7 +155,10 @@ class Transaction {
);
}
addOutput(scriptPubKey, value) {
- typeforce(types.tuple(types.Buffer, types.Satoshi), arguments);
+ v.parse(v.tuple([types.BufferSchema, types.SatoshiSchema]), [
+ scriptPubKey,
+ value,
+ ]);
// Add the output and return the output's index
return (
this.outs.push({
@@ -145,20 +172,41 @@ class Transaction {
return x.witness.length !== 0;
});
}
+ /**
+ * Check if this is a Litecoin MWEB peg-out transaction.
+ * MWEB peg-out transactions have:
+ * - At least one output starting with OP_8 (witness version 8)
+ * - At least one input with empty script (anyone-can-spend from HogEx)
+ */
+ isMwebPegOutTx() {
+ return (
+ this.outs.some(output => {
+ // Check if output script starts with OP_8 (0x58)
+ return output.script.length > 0 && output.script[0] === opcodes.OP_8;
+ }) && this.ins.some(input => input.script.length === 0)
+ );
+ }
+ stripWitnesses() {
+ this.ins.forEach(input => {
+ input.witness = EMPTY_WITNESS; // Set witness data to an empty array
+ });
+ }
weight() {
- const base = this.byteLength(false);
- const total = this.byteLength(true);
+ // Weight calculation excludes MWEB-specific bytes
+ const base = this.byteLength(false, false);
+ const total = this.byteLength(true, false);
return base * 3 + total;
}
virtualSize() {
return Math.ceil(this.weight() / 4);
}
- byteLength(_ALLOW_WITNESS = true) {
+ byteLength(_ALLOW_WITNESS = true, _ALLOW_MWEB = true) {
const hasWitnesses = _ALLOW_WITNESS && this.hasWitnesses();
+ const hasMweb = _ALLOW_MWEB && !hasWitnesses && this.isMwebPegOutTx();
return (
(hasWitnesses ? 10 : 8) +
- bufferutils_1.varuint.encodingLength(this.ins.length) +
- bufferutils_1.varuint.encodingLength(this.outs.length) +
+ varuint.encodingLength(this.ins.length) +
+ varuint.encodingLength(this.outs.length) +
this.ins.reduce((sum, input) => {
return sum + 40 + varSliceSize(input.script);
}, 0) +
@@ -169,7 +217,8 @@ class Transaction {
? this.ins.reduce((sum, input) => {
return sum + vectorSize(input.witness);
}, 0)
- : 0)
+ : 0) +
+ (hasMweb ? 3 : 0) // marker (1) + flag (1) + extra byte before locktime (1)
);
}
clone() {
@@ -202,16 +251,17 @@ class Transaction {
* This hash can then be used to sign the provided transaction input.
*/
hashForSignature(inIndex, prevOutScript, hashType) {
- typeforce(
- types.tuple(types.UInt32, types.Buffer, /* types.UInt8 */ types.Number),
- arguments,
- );
+ v.parse(v.tuple([types.UInt32Schema, types.BufferSchema, v.number()]), [
+ inIndex,
+ prevOutScript,
+ hashType,
+ ]);
// https://github.com/bitcoin/bitcoin/blob/master/src/test/sighash_tests.cpp#L29
if (inIndex >= this.ins.length) return ONE;
// ignore OP_CODESEPARATOR
const ourScript = bscript.compile(
bscript.decompile(prevOutScript).filter(x => {
- return x !== script_1.OPS.OP_CODESEPARATOR;
+ return x !== opcodes.OP_CODESEPARATOR;
}),
);
const txTmp = this.clone();
@@ -252,21 +302,21 @@ class Transaction {
txTmp.ins[inIndex].script = ourScript;
}
// serialize and hash
- const buffer = Buffer.allocUnsafe(txTmp.byteLength(false) + 4);
- buffer.writeInt32LE(hashType, buffer.length - 4);
+ const buffer = new Uint8Array(txTmp.byteLength(false) + 4);
+ tools.writeInt32(buffer, buffer.length - 4, hashType, 'LE');
txTmp.__toBuffer(buffer, 0, false);
return bcrypto.hash256(buffer);
}
hashForWitnessV1(inIndex, prevOutScripts, values, hashType, leafHash, annex) {
// https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#common-signature-message
- typeforce(
- types.tuple(
- types.UInt32,
- typeforce.arrayOf(types.Buffer),
- typeforce.arrayOf(types.Satoshi),
- types.UInt32,
- ),
- arguments,
+ v.parse(
+ v.tuple([
+ types.UInt32Schema,
+ v.array(types.BufferSchema),
+ v.array(types.SatoshiSchema),
+ types.UInt32Schema,
+ ]),
+ [inIndex, prevOutScripts, values, hashType],
);
if (
values.length !== this.ins.length ||
@@ -288,50 +338,46 @@ class Transaction {
let hashSequences = EMPTY_BUFFER;
let hashOutputs = EMPTY_BUFFER;
if (!isAnyoneCanPay) {
- let bufferWriter = bufferutils_1.BufferWriter.withCapacity(
- 36 * this.ins.length,
- );
+ let bufferWriter = BufferWriter.withCapacity(36 * this.ins.length);
this.ins.forEach(txIn => {
bufferWriter.writeSlice(txIn.hash);
bufferWriter.writeUInt32(txIn.index);
});
- hashPrevouts = bcrypto.sha256(bufferWriter.end());
- bufferWriter = bufferutils_1.BufferWriter.withCapacity(
- 8 * this.ins.length,
- );
- values.forEach(value => bufferWriter.writeUInt64(value));
- hashAmounts = bcrypto.sha256(bufferWriter.end());
- bufferWriter = bufferutils_1.BufferWriter.withCapacity(
+ hashPrevouts = sha256(bufferWriter.end());
+ bufferWriter = BufferWriter.withCapacity(8 * this.ins.length);
+ values.forEach(value => bufferWriter.writeInt64(value));
+ hashAmounts = sha256(bufferWriter.end());
+ bufferWriter = BufferWriter.withCapacity(
prevOutScripts.map(varSliceSize).reduce((a, b) => a + b),
);
prevOutScripts.forEach(prevOutScript =>
bufferWriter.writeVarSlice(prevOutScript),
);
- hashScriptPubKeys = bcrypto.sha256(bufferWriter.end());
- bufferWriter = bufferutils_1.BufferWriter.withCapacity(
- 4 * this.ins.length,
- );
+ hashScriptPubKeys = sha256(bufferWriter.end());
+ bufferWriter = BufferWriter.withCapacity(4 * this.ins.length);
this.ins.forEach(txIn => bufferWriter.writeUInt32(txIn.sequence));
- hashSequences = bcrypto.sha256(bufferWriter.end());
+ hashSequences = sha256(bufferWriter.end());
}
if (!(isNone || isSingle)) {
+ if (!this.outs.length)
+ throw new Error('Add outputs to the transaction before signing.');
const txOutsSize = this.outs
.map(output => 8 + varSliceSize(output.script))
.reduce((a, b) => a + b);
- const bufferWriter = bufferutils_1.BufferWriter.withCapacity(txOutsSize);
+ const bufferWriter = BufferWriter.withCapacity(txOutsSize);
this.outs.forEach(out => {
- bufferWriter.writeUInt64(out.value);
+ bufferWriter.writeInt64(out.value);
bufferWriter.writeVarSlice(out.script);
});
- hashOutputs = bcrypto.sha256(bufferWriter.end());
+ hashOutputs = sha256(bufferWriter.end());
} else if (isSingle && inIndex < this.outs.length) {
const output = this.outs[inIndex];
- const bufferWriter = bufferutils_1.BufferWriter.withCapacity(
+ const bufferWriter = BufferWriter.withCapacity(
8 + varSliceSize(output.script),
);
- bufferWriter.writeUInt64(output.value);
+ bufferWriter.writeInt64(output.value);
bufferWriter.writeVarSlice(output.script);
- hashOutputs = bcrypto.sha256(bufferWriter.end());
+ hashOutputs = sha256(bufferWriter.end());
}
const spendType = (leafHash ? 2 : 0) + (annex ? 1 : 0);
// Length calculation from:
@@ -344,10 +390,10 @@ class Transaction {
(isNone ? 32 : 0) +
(annex ? 32 : 0) +
(leafHash ? 37 : 0);
- const sigMsgWriter = bufferutils_1.BufferWriter.withCapacity(sigMsgSize);
+ const sigMsgWriter = BufferWriter.withCapacity(sigMsgSize);
sigMsgWriter.writeUInt8(hashType);
// Transaction
- sigMsgWriter.writeInt32(this.version);
+ sigMsgWriter.writeUInt32(this.version);
sigMsgWriter.writeUInt32(this.locktime);
sigMsgWriter.writeSlice(hashPrevouts);
sigMsgWriter.writeSlice(hashAmounts);
@@ -362,18 +408,16 @@ class Transaction {
const input = this.ins[inIndex];
sigMsgWriter.writeSlice(input.hash);
sigMsgWriter.writeUInt32(input.index);
- sigMsgWriter.writeUInt64(values[inIndex]);
+ sigMsgWriter.writeInt64(values[inIndex]);
sigMsgWriter.writeVarSlice(prevOutScripts[inIndex]);
sigMsgWriter.writeUInt32(input.sequence);
} else {
sigMsgWriter.writeUInt32(inIndex);
}
if (annex) {
- const bufferWriter = bufferutils_1.BufferWriter.withCapacity(
- varSliceSize(annex),
- );
+ const bufferWriter = BufferWriter.withCapacity(varSliceSize(annex));
bufferWriter.writeVarSlice(annex);
- sigMsgWriter.writeSlice(bcrypto.sha256(bufferWriter.end()));
+ sigMsgWriter.writeSlice(sha256(bufferWriter.end()));
}
// Output
if (isSingle) {
@@ -389,22 +433,27 @@ class Transaction {
// https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#cite_note-19
return bcrypto.taggedHash(
'TapSighash',
- Buffer.concat([Buffer.of(0x00), sigMsgWriter.end()]),
+ tools.concat([Uint8Array.from([0x00]), sigMsgWriter.end()]),
);
}
hashForWitnessV0(inIndex, prevOutScript, value, hashType) {
- typeforce(
- types.tuple(types.UInt32, types.Buffer, types.Satoshi, types.UInt32),
- arguments,
+ v.parse(
+ v.tuple([
+ types.UInt32Schema,
+ types.BufferSchema,
+ types.SatoshiSchema,
+ types.UInt32Schema,
+ ]),
+ [inIndex, prevOutScript, value, hashType],
);
- let tbuffer = Buffer.from([]);
+ let tbuffer = Uint8Array.from([]);
let bufferWriter;
let hashOutputs = ZERO;
let hashPrevouts = ZERO;
let hashSequence = ZERO;
if (!(hashType & Transaction.SIGHASH_ANYONECANPAY)) {
- tbuffer = Buffer.allocUnsafe(36 * this.ins.length);
- bufferWriter = new bufferutils_1.BufferWriter(tbuffer, 0);
+ tbuffer = new Uint8Array(36 * this.ins.length);
+ bufferWriter = new BufferWriter(tbuffer, 0);
this.ins.forEach(txIn => {
bufferWriter.writeSlice(txIn.hash);
bufferWriter.writeUInt32(txIn.index);
@@ -416,8 +465,8 @@ class Transaction {
(hashType & 0x1f) !== Transaction.SIGHASH_SINGLE &&
(hashType & 0x1f) !== Transaction.SIGHASH_NONE
) {
- tbuffer = Buffer.allocUnsafe(4 * this.ins.length);
- bufferWriter = new bufferutils_1.BufferWriter(tbuffer, 0);
+ tbuffer = new Uint8Array(4 * this.ins.length);
+ bufferWriter = new BufferWriter(tbuffer, 0);
this.ins.forEach(txIn => {
bufferWriter.writeUInt32(txIn.sequence);
});
@@ -430,10 +479,10 @@ class Transaction {
const txOutsSize = this.outs.reduce((sum, output) => {
return sum + 8 + varSliceSize(output.script);
}, 0);
- tbuffer = Buffer.allocUnsafe(txOutsSize);
- bufferWriter = new bufferutils_1.BufferWriter(tbuffer, 0);
+ tbuffer = new Uint8Array(txOutsSize);
+ bufferWriter = new BufferWriter(tbuffer, 0);
this.outs.forEach(out => {
- bufferWriter.writeUInt64(out.value);
+ bufferWriter.writeInt64(out.value);
bufferWriter.writeVarSlice(out.script);
});
hashOutputs = bcrypto.hash256(tbuffer);
@@ -442,64 +491,78 @@ class Transaction {
inIndex < this.outs.length
) {
const output = this.outs[inIndex];
- tbuffer = Buffer.allocUnsafe(8 + varSliceSize(output.script));
- bufferWriter = new bufferutils_1.BufferWriter(tbuffer, 0);
- bufferWriter.writeUInt64(output.value);
+ tbuffer = new Uint8Array(8 + varSliceSize(output.script));
+ bufferWriter = new BufferWriter(tbuffer, 0);
+ bufferWriter.writeInt64(output.value);
bufferWriter.writeVarSlice(output.script);
hashOutputs = bcrypto.hash256(tbuffer);
}
- tbuffer = Buffer.allocUnsafe(156 + varSliceSize(prevOutScript));
- bufferWriter = new bufferutils_1.BufferWriter(tbuffer, 0);
+ tbuffer = new Uint8Array(156 + varSliceSize(prevOutScript));
+ bufferWriter = new BufferWriter(tbuffer, 0);
const input = this.ins[inIndex];
- bufferWriter.writeInt32(this.version);
+ bufferWriter.writeUInt32(this.version);
bufferWriter.writeSlice(hashPrevouts);
bufferWriter.writeSlice(hashSequence);
bufferWriter.writeSlice(input.hash);
bufferWriter.writeUInt32(input.index);
bufferWriter.writeVarSlice(prevOutScript);
- bufferWriter.writeUInt64(value);
+ bufferWriter.writeInt64(value);
bufferWriter.writeUInt32(input.sequence);
bufferWriter.writeSlice(hashOutputs);
bufferWriter.writeUInt32(this.locktime);
bufferWriter.writeUInt32(hashType);
return bcrypto.hash256(tbuffer);
}
- getHash(forWitness) {
+ getHash(forWitness, forMweb) {
// wtxid for coinbase is always 32 bytes of 0x00
- if (forWitness && this.isCoinbase()) return Buffer.alloc(32, 0);
- return bcrypto.hash256(this.__toBuffer(undefined, undefined, forWitness));
+ if (forWitness && this.isCoinbase()) return new Uint8Array(32);
+ // For MWEB transactions, txid is calculated WITHOUT the MWEB marker/flag/extra byte
+ // (similar to how SegWit txid excludes witness data)
+ const allowMweb = forMweb ?? false;
+ return bcrypto.hash256(
+ this.__toBuffer(undefined, undefined, forWitness, allowMweb),
+ );
}
getId() {
// transaction hash's are displayed in reverse order
- return (0, bufferutils_1.reverseBuffer)(this.getHash(false)).toString(
- 'hex',
- );
+ // txid calculation excludes both witness and MWEB-specific data
+ return tools.toHex(reverseBuffer(this.getHash(false, false)));
}
toBuffer(buffer, initialOffset) {
return this.__toBuffer(buffer, initialOffset, true);
}
toHex() {
- return this.toBuffer(undefined, undefined).toString('hex');
+ return tools.toHex(this.toBuffer(undefined, undefined));
}
setInputScript(index, scriptSig) {
- typeforce(types.tuple(types.Number, types.Buffer), arguments);
+ v.parse(v.tuple([v.number(), types.BufferSchema]), [index, scriptSig]);
this.ins[index].script = scriptSig;
}
setWitness(index, witness) {
- typeforce(types.tuple(types.Number, [types.Buffer]), arguments);
+ v.parse(v.tuple([v.number(), v.array(types.BufferSchema)]), [
+ index,
+ witness,
+ ]);
this.ins[index].witness = witness;
}
- __toBuffer(buffer, initialOffset, _ALLOW_WITNESS = false) {
- if (!buffer) buffer = Buffer.allocUnsafe(this.byteLength(_ALLOW_WITNESS));
- const bufferWriter = new bufferutils_1.BufferWriter(
- buffer,
- initialOffset || 0,
- );
- bufferWriter.writeInt32(this.version);
+ __toBuffer(
+ buffer,
+ initialOffset,
+ _ALLOW_WITNESS = false,
+ _ALLOW_MWEB = true,
+ ) {
+ if (!buffer)
+ buffer = new Uint8Array(this.byteLength(_ALLOW_WITNESS, _ALLOW_MWEB));
+ const bufferWriter = new BufferWriter(buffer, initialOffset || 0);
+ bufferWriter.writeUInt32(this.version);
const hasWitnesses = _ALLOW_WITNESS && this.hasWitnesses();
+ const hasMweb = _ALLOW_MWEB && !hasWitnesses && this.isMwebPegOutTx();
if (hasWitnesses) {
bufferWriter.writeUInt8(Transaction.ADVANCED_TRANSACTION_MARKER);
bufferWriter.writeUInt8(Transaction.ADVANCED_TRANSACTION_FLAG);
+ } else if (hasMweb) {
+ bufferWriter.writeUInt8(Transaction.ADVANCED_TRANSACTION_MARKER);
+ bufferWriter.writeUInt8(Transaction.MWEB_PEGOUT_FLAG);
}
bufferWriter.writeVarInt(this.ins.length);
this.ins.forEach(txIn => {
@@ -511,7 +574,7 @@ class Transaction {
bufferWriter.writeVarInt(this.outs.length);
this.outs.forEach(txOut => {
if (isOutput(txOut)) {
- bufferWriter.writeUInt64(txOut.value);
+ bufferWriter.writeInt64(txOut.value);
} else {
bufferWriter.writeSlice(txOut.valueBuffer);
}
@@ -522,6 +585,10 @@ class Transaction {
bufferWriter.writeVector(input.witness);
});
}
+ // MWEB peg-out transactions have an extra 0x00 byte before locktime
+ if (hasMweb) {
+ bufferWriter.writeUInt8(0);
+ }
bufferWriter.writeUInt32(this.locktime);
// avoid slicing unless necessary
if (initialOffset !== undefined)
@@ -529,14 +596,3 @@ class Transaction {
return buffer;
}
}
-exports.Transaction = Transaction;
-Transaction.DEFAULT_SEQUENCE = 0xffffffff;
-Transaction.SIGHASH_DEFAULT = 0x00;
-Transaction.SIGHASH_ALL = 0x01;
-Transaction.SIGHASH_NONE = 0x02;
-Transaction.SIGHASH_SINGLE = 0x03;
-Transaction.SIGHASH_ANYONECANPAY = 0x80;
-Transaction.SIGHASH_OUTPUT_MASK = 0x03;
-Transaction.SIGHASH_INPUT_MASK = 0x80;
-Transaction.ADVANCED_TRANSACTION_MARKER = 0x00;
-Transaction.ADVANCED_TRANSACTION_FLAG = 0x01;
diff --git a/src/esm/types.js b/src/esm/types.js
new file mode 100644
index 000000000..8e406a338
--- /dev/null
+++ b/src/esm/types.js
@@ -0,0 +1,83 @@
+import * as tools from 'uint8array-tools';
+import * as v from 'valibot';
+const ZERO32 = new Uint8Array(32);
+const EC_P = tools.fromHex(
+ 'fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f',
+);
+export const NBufferSchemaFactory = size =>
+ v.pipe(v.instance(Uint8Array), v.length(size));
+/**
+ * Checks if two arrays of Buffers are equal.
+ * @param a - The first array of Buffers.
+ * @param b - The second array of Buffers.
+ * @returns True if the arrays are equal, false otherwise.
+ */
+export function stacksEqual(a, b) {
+ if (a.length !== b.length) return false;
+ return a.every((x, i) => {
+ return tools.compare(x, b[i]) === 0;
+ });
+}
+/**
+ * Checks if the given value is a valid elliptic curve point.
+ * @param p - The value to check.
+ * @returns True if the value is a valid elliptic curve point, false otherwise.
+ */
+export function isPoint(p) {
+ if (!(p instanceof Uint8Array)) return false;
+ if (p.length < 33) return false;
+ const t = p[0];
+ const x = p.slice(1, 33);
+ if (tools.compare(ZERO32, x) === 0) return false;
+ if (tools.compare(x, EC_P) >= 0) return false;
+ if ((t === 0x02 || t === 0x03) && p.length === 33) {
+ return true;
+ }
+ const y = p.slice(33);
+ if (tools.compare(ZERO32, y) === 0) return false;
+ if (tools.compare(y, EC_P) >= 0) return false;
+ if (t === 0x04 && p.length === 65) return true;
+ return false;
+}
+export const TAPLEAF_VERSION_MASK = 0xfe;
+export function isTapleaf(o) {
+ if (!o || !('output' in o)) return false;
+ if (!(o.output instanceof Uint8Array)) return false;
+ if (o.version !== undefined)
+ return (o.version & TAPLEAF_VERSION_MASK) === o.version;
+ return true;
+}
+export function isTaptree(scriptTree) {
+ if (!Array.isArray(scriptTree)) return isTapleaf(scriptTree);
+ if (scriptTree.length !== 2) return false;
+ return scriptTree.every(t => isTaptree(t));
+}
+export const Buffer256bitSchema = NBufferSchemaFactory(32);
+export const Hash160bitSchema = NBufferSchemaFactory(20);
+export const Hash256bitSchema = NBufferSchemaFactory(32);
+export const BufferSchema = v.instance(Uint8Array);
+export const HexSchema = v.pipe(v.string(), v.regex(/^([0-9a-f]{2})+$/i));
+export const UInt8Schema = v.pipe(
+ v.number(),
+ v.integer(),
+ v.minValue(0),
+ v.maxValue(0xff),
+);
+export const UInt32Schema = v.pipe(
+ v.number(),
+ v.integer(),
+ v.minValue(0),
+ v.maxValue(0xffffffff),
+);
+export const SatoshiSchema = v.pipe(
+ v.bigint(),
+ v.minValue(0n),
+ v.maxValue(0x7fffffffffffffffn),
+);
+export const NullablePartial = a =>
+ v.object(
+ Object.entries(a).reduce(
+ (acc, next) => ({ ...acc, [next[0]]: v.nullish(next[1]) }),
+ {},
+ ),
+ );
diff --git a/src/index.d.ts b/src/index.d.ts
deleted file mode 100644
index b93c2aa40..000000000
--- a/src/index.d.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-import * as address from './address';
-import * as crypto from './crypto';
-import * as networks from './networks';
-import * as payments from './payments';
-import * as script from './script';
-export { address, crypto, networks, payments, script };
-export { Block } from './block';
-export { TaggedHashPrefix } from './crypto';
-export { Psbt, PsbtTxInput, PsbtTxOutput, Signer, SignerAsync, HDSigner, HDSignerAsync, } from './psbt';
-export { OPS as opcodes } from './ops';
-export { Transaction } from './transaction';
-export { Network } from './networks';
-export { Payment, PaymentCreator, PaymentOpts, Stack, StackElement, } from './payments';
-export { Input as TxInput, Output as TxOutput } from './transaction';
diff --git a/src/index.js b/src/index.js
deleted file mode 100644
index 983b0cc76..000000000
--- a/src/index.js
+++ /dev/null
@@ -1,41 +0,0 @@
-'use strict';
-Object.defineProperty(exports, '__esModule', { value: true });
-exports.Transaction = exports.opcodes = exports.Psbt = exports.Block = exports.script = exports.payments = exports.networks = exports.crypto = exports.address = void 0;
-const address = require('./address');
-exports.address = address;
-const crypto = require('./crypto');
-exports.crypto = crypto;
-const networks = require('./networks');
-exports.networks = networks;
-const payments = require('./payments');
-exports.payments = payments;
-const script = require('./script');
-exports.script = script;
-var block_1 = require('./block');
-Object.defineProperty(exports, 'Block', {
- enumerable: true,
- get: function() {
- return block_1.Block;
- },
-});
-var psbt_1 = require('./psbt');
-Object.defineProperty(exports, 'Psbt', {
- enumerable: true,
- get: function() {
- return psbt_1.Psbt;
- },
-});
-var ops_1 = require('./ops');
-Object.defineProperty(exports, 'opcodes', {
- enumerable: true,
- get: function() {
- return ops_1.OPS;
- },
-});
-var transaction_1 = require('./transaction');
-Object.defineProperty(exports, 'Transaction', {
- enumerable: true,
- get: function() {
- return transaction_1.Transaction;
- },
-});
diff --git a/src/merkle.d.ts b/src/merkle.d.ts
deleted file mode 100644
index d602201b9..000000000
--- a/src/merkle.d.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-///
-export declare function fastMerkleRoot(values: Buffer[], digestFn: (b: Buffer) => Buffer): Buffer;
diff --git a/src/merkle.js b/src/merkle.js
deleted file mode 100644
index e93f9cab6..000000000
--- a/src/merkle.js
+++ /dev/null
@@ -1,22 +0,0 @@
-'use strict';
-Object.defineProperty(exports, '__esModule', { value: true });
-exports.fastMerkleRoot = void 0;
-function fastMerkleRoot(values, digestFn) {
- if (!Array.isArray(values)) throw TypeError('Expected values Array');
- if (typeof digestFn !== 'function')
- throw TypeError('Expected digest Function');
- let length = values.length;
- const results = values.concat();
- while (length > 1) {
- let j = 0;
- for (let i = 0; i < length; i += 2, ++j) {
- const left = results[i];
- const right = i + 1 === length ? left : results[i + 1];
- const data = Buffer.concat([left, right]);
- results[j] = digestFn(data);
- }
- length = j;
- }
- return results[0];
-}
-exports.fastMerkleRoot = fastMerkleRoot;
diff --git a/src/networks.d.ts b/src/networks.d.ts
deleted file mode 100644
index d5590fd1b..000000000
--- a/src/networks.d.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-export interface Network {
- messagePrefix: string;
- bech32: string;
- bip32: Bip32;
- pubKeyHash: number;
- scriptHash: number;
- wif: number;
-}
-interface Bip32 {
- public: number;
- private: number;
-}
-export declare const bitcoin: Network;
-export declare const regtest: Network;
-export declare const testnet: Network;
-export {};
diff --git a/src/ops.d.ts b/src/ops.d.ts
deleted file mode 100644
index cda7a84a6..000000000
--- a/src/ops.d.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-declare const OPS: {
- [key: string]: number;
-};
-declare const REVERSE_OPS: {
- [key: number]: string;
-};
-export { OPS, REVERSE_OPS };
diff --git a/src/ops.js b/src/ops.js
deleted file mode 100644
index 9d629cd00..000000000
--- a/src/ops.js
+++ /dev/null
@@ -1,130 +0,0 @@
-'use strict';
-Object.defineProperty(exports, '__esModule', { value: true });
-exports.REVERSE_OPS = exports.OPS = void 0;
-const OPS = {
- OP_FALSE: 0,
- OP_0: 0,
- OP_PUSHDATA1: 76,
- OP_PUSHDATA2: 77,
- OP_PUSHDATA4: 78,
- OP_1NEGATE: 79,
- OP_RESERVED: 80,
- OP_TRUE: 81,
- OP_1: 81,
- OP_2: 82,
- OP_3: 83,
- OP_4: 84,
- OP_5: 85,
- OP_6: 86,
- OP_7: 87,
- OP_8: 88,
- OP_9: 89,
- OP_10: 90,
- OP_11: 91,
- OP_12: 92,
- OP_13: 93,
- OP_14: 94,
- OP_15: 95,
- OP_16: 96,
- OP_NOP: 97,
- OP_VER: 98,
- OP_IF: 99,
- OP_NOTIF: 100,
- OP_VERIF: 101,
- OP_VERNOTIF: 102,
- OP_ELSE: 103,
- OP_ENDIF: 104,
- OP_VERIFY: 105,
- OP_RETURN: 106,
- OP_TOALTSTACK: 107,
- OP_FROMALTSTACK: 108,
- OP_2DROP: 109,
- OP_2DUP: 110,
- OP_3DUP: 111,
- OP_2OVER: 112,
- OP_2ROT: 113,
- OP_2SWAP: 114,
- OP_IFDUP: 115,
- OP_DEPTH: 116,
- OP_DROP: 117,
- OP_DUP: 118,
- OP_NIP: 119,
- OP_OVER: 120,
- OP_PICK: 121,
- OP_ROLL: 122,
- OP_ROT: 123,
- OP_SWAP: 124,
- OP_TUCK: 125,
- OP_CAT: 126,
- OP_SUBSTR: 127,
- OP_LEFT: 128,
- OP_RIGHT: 129,
- OP_SIZE: 130,
- OP_INVERT: 131,
- OP_AND: 132,
- OP_OR: 133,
- OP_XOR: 134,
- OP_EQUAL: 135,
- OP_EQUALVERIFY: 136,
- OP_RESERVED1: 137,
- OP_RESERVED2: 138,
- OP_1ADD: 139,
- OP_1SUB: 140,
- OP_2MUL: 141,
- OP_2DIV: 142,
- OP_NEGATE: 143,
- OP_ABS: 144,
- OP_NOT: 145,
- OP_0NOTEQUAL: 146,
- OP_ADD: 147,
- OP_SUB: 148,
- OP_MUL: 149,
- OP_DIV: 150,
- OP_MOD: 151,
- OP_LSHIFT: 152,
- OP_RSHIFT: 153,
- OP_BOOLAND: 154,
- OP_BOOLOR: 155,
- OP_NUMEQUAL: 156,
- OP_NUMEQUALVERIFY: 157,
- OP_NUMNOTEQUAL: 158,
- OP_LESSTHAN: 159,
- OP_GREATERTHAN: 160,
- OP_LESSTHANOREQUAL: 161,
- OP_GREATERTHANOREQUAL: 162,
- OP_MIN: 163,
- OP_MAX: 164,
- OP_WITHIN: 165,
- OP_RIPEMD160: 166,
- OP_SHA1: 167,
- OP_SHA256: 168,
- OP_HASH160: 169,
- OP_HASH256: 170,
- OP_CODESEPARATOR: 171,
- OP_CHECKSIG: 172,
- OP_CHECKSIGVERIFY: 173,
- OP_CHECKMULTISIG: 174,
- OP_CHECKMULTISIGVERIFY: 175,
- OP_NOP1: 176,
- OP_NOP2: 177,
- OP_CHECKLOCKTIMEVERIFY: 177,
- OP_NOP3: 178,
- OP_CHECKSEQUENCEVERIFY: 178,
- OP_NOP4: 179,
- OP_NOP5: 180,
- OP_NOP6: 181,
- OP_NOP7: 182,
- OP_NOP8: 183,
- OP_NOP9: 184,
- OP_NOP10: 185,
- OP_PUBKEYHASH: 253,
- OP_PUBKEY: 254,
- OP_INVALIDOPCODE: 255,
-};
-exports.OPS = OPS;
-const REVERSE_OPS = {};
-exports.REVERSE_OPS = REVERSE_OPS;
-for (const op of Object.keys(OPS)) {
- const code = OPS[op];
- REVERSE_OPS[code] = op;
-}
diff --git a/src/payments/embed.d.ts b/src/payments/embed.d.ts
deleted file mode 100644
index 76a9ed28f..000000000
--- a/src/payments/embed.d.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-import { Payment, PaymentOpts } from './index';
-export declare function p2data(a: Payment, opts?: PaymentOpts): Payment;
diff --git a/src/payments/index.d.ts b/src/payments/index.d.ts
deleted file mode 100644
index 1edf07167..000000000
--- a/src/payments/index.d.ts
+++ /dev/null
@@ -1,36 +0,0 @@
-///
-import { Network } from '../networks';
-import { p2data as embed } from './embed';
-import { p2ms } from './p2ms';
-import { p2pk } from './p2pk';
-import { p2pkh } from './p2pkh';
-import { p2sh } from './p2sh';
-import { p2wpkh } from './p2wpkh';
-import { p2wsh } from './p2wsh';
-export interface Payment {
- name?: string;
- network?: Network;
- output?: Buffer;
- data?: Buffer[];
- m?: number;
- n?: number;
- pubkeys?: Buffer[];
- input?: Buffer;
- signatures?: Buffer[];
- pubkey?: Buffer;
- signature?: Buffer;
- address?: string;
- hash?: Buffer;
- redeem?: Payment;
- witness?: Buffer[];
-}
-export declare type PaymentCreator = (a: Payment, opts?: PaymentOpts) => Payment;
-export declare type PaymentFunction = () => Payment;
-export interface PaymentOpts {
- validate?: boolean;
- allowIncomplete?: boolean;
-}
-export declare type StackElement = Buffer | number;
-export declare type Stack = StackElement[];
-export declare type StackFunction = () => Stack;
-export { embed, p2ms, p2pk, p2pkh, p2sh, p2wpkh, p2wsh };
diff --git a/src/payments/index.js b/src/payments/index.js
deleted file mode 100644
index c23c529c6..000000000
--- a/src/payments/index.js
+++ /dev/null
@@ -1,54 +0,0 @@
-'use strict';
-Object.defineProperty(exports, '__esModule', { value: true });
-exports.p2wsh = exports.p2wpkh = exports.p2sh = exports.p2pkh = exports.p2pk = exports.p2ms = exports.embed = void 0;
-const embed_1 = require('./embed');
-Object.defineProperty(exports, 'embed', {
- enumerable: true,
- get: function() {
- return embed_1.p2data;
- },
-});
-const p2ms_1 = require('./p2ms');
-Object.defineProperty(exports, 'p2ms', {
- enumerable: true,
- get: function() {
- return p2ms_1.p2ms;
- },
-});
-const p2pk_1 = require('./p2pk');
-Object.defineProperty(exports, 'p2pk', {
- enumerable: true,
- get: function() {
- return p2pk_1.p2pk;
- },
-});
-const p2pkh_1 = require('./p2pkh');
-Object.defineProperty(exports, 'p2pkh', {
- enumerable: true,
- get: function() {
- return p2pkh_1.p2pkh;
- },
-});
-const p2sh_1 = require('./p2sh');
-Object.defineProperty(exports, 'p2sh', {
- enumerable: true,
- get: function() {
- return p2sh_1.p2sh;
- },
-});
-const p2wpkh_1 = require('./p2wpkh');
-Object.defineProperty(exports, 'p2wpkh', {
- enumerable: true,
- get: function() {
- return p2wpkh_1.p2wpkh;
- },
-});
-const p2wsh_1 = require('./p2wsh');
-Object.defineProperty(exports, 'p2wsh', {
- enumerable: true,
- get: function() {
- return p2wsh_1.p2wsh;
- },
-});
-// TODO
-// witness commitment
diff --git a/src/payments/lazy.d.ts b/src/payments/lazy.d.ts
deleted file mode 100644
index 3463906d8..000000000
--- a/src/payments/lazy.d.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-export declare function prop(object: {}, name: string, f: () => any): void;
-export declare function value(f: () => T): () => T;
diff --git a/src/payments/p2ms.d.ts b/src/payments/p2ms.d.ts
deleted file mode 100644
index 199e02915..000000000
--- a/src/payments/p2ms.d.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-import { Payment, PaymentOpts } from './index';
-export declare function p2ms(a: Payment, opts?: PaymentOpts): Payment;
diff --git a/src/payments/p2pk.d.ts b/src/payments/p2pk.d.ts
deleted file mode 100644
index d7e824d6c..000000000
--- a/src/payments/p2pk.d.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-import { Payment, PaymentOpts } from './index';
-export declare function p2pk(a: Payment, opts?: PaymentOpts): Payment;
diff --git a/src/payments/p2pkh.d.ts b/src/payments/p2pkh.d.ts
deleted file mode 100644
index a33eeb030..000000000
--- a/src/payments/p2pkh.d.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-import { Payment, PaymentOpts } from './index';
-export declare function p2pkh(a: Payment, opts?: PaymentOpts): Payment;
diff --git a/src/payments/p2sh.d.ts b/src/payments/p2sh.d.ts
deleted file mode 100644
index bb76772e7..000000000
--- a/src/payments/p2sh.d.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-import { Payment, PaymentOpts } from './index';
-export declare function p2sh(a: Payment, opts?: PaymentOpts): Payment;
diff --git a/src/payments/p2wpkh.d.ts b/src/payments/p2wpkh.d.ts
deleted file mode 100644
index 360939119..000000000
--- a/src/payments/p2wpkh.d.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-import { Payment, PaymentOpts } from './index';
-export declare function p2wpkh(a: Payment, opts?: PaymentOpts): Payment;
diff --git a/src/payments/p2wsh.d.ts b/src/payments/p2wsh.d.ts
deleted file mode 100644
index d9ae925e8..000000000
--- a/src/payments/p2wsh.d.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-import { Payment, PaymentOpts } from './index';
-export declare function p2wsh(a: Payment, opts?: PaymentOpts): Payment;
diff --git a/src/push_data.d.ts b/src/push_data.d.ts
deleted file mode 100644
index 07c2f918d..000000000
--- a/src/push_data.d.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-///
-export declare function encodingLength(i: number): number;
-export declare function encode(buffer: Buffer, num: number, offset: number): number;
-export declare function decode(buffer: Buffer, offset: number): {
- opcode: number;
- number: number;
- size: number;
-} | null;
diff --git a/src/push_data.js b/src/push_data.js
deleted file mode 100644
index 16b6147b5..000000000
--- a/src/push_data.js
+++ /dev/null
@@ -1,61 +0,0 @@
-'use strict';
-Object.defineProperty(exports, '__esModule', { value: true });
-exports.decode = exports.encode = exports.encodingLength = void 0;
-const ops_1 = require('./ops');
-function encodingLength(i) {
- return i < ops_1.OPS.OP_PUSHDATA1 ? 1 : i <= 0xff ? 2 : i <= 0xffff ? 3 : 5;
-}
-exports.encodingLength = encodingLength;
-function encode(buffer, num, offset) {
- const size = encodingLength(num);
- // ~6 bit
- if (size === 1) {
- buffer.writeUInt8(num, offset);
- // 8 bit
- } else if (size === 2) {
- buffer.writeUInt8(ops_1.OPS.OP_PUSHDATA1, offset);
- buffer.writeUInt8(num, offset + 1);
- // 16 bit
- } else if (size === 3) {
- buffer.writeUInt8(ops_1.OPS.OP_PUSHDATA2, offset);
- buffer.writeUInt16LE(num, offset + 1);
- // 32 bit
- } else {
- buffer.writeUInt8(ops_1.OPS.OP_PUSHDATA4, offset);
- buffer.writeUInt32LE(num, offset + 1);
- }
- return size;
-}
-exports.encode = encode;
-function decode(buffer, offset) {
- const opcode = buffer.readUInt8(offset);
- let num;
- let size;
- // ~6 bit
- if (opcode < ops_1.OPS.OP_PUSHDATA1) {
- num = opcode;
- size = 1;
- // 8 bit
- } else if (opcode === ops_1.OPS.OP_PUSHDATA1) {
- if (offset + 2 > buffer.length) return null;
- num = buffer.readUInt8(offset + 1);
- size = 2;
- // 16 bit
- } else if (opcode === ops_1.OPS.OP_PUSHDATA2) {
- if (offset + 3 > buffer.length) return null;
- num = buffer.readUInt16LE(offset + 1);
- size = 3;
- // 32 bit
- } else {
- if (offset + 5 > buffer.length) return null;
- if (opcode !== ops_1.OPS.OP_PUSHDATA4) throw new Error('Unexpected opcode');
- num = buffer.readUInt32LE(offset + 1);
- size = 5;
- }
- return {
- opcode,
- number: num,
- size,
- };
-}
-exports.decode = decode;
diff --git a/src/script.d.ts b/src/script.d.ts
deleted file mode 100644
index 261ecf4af..000000000
--- a/src/script.d.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-///
-import { OPS } from './ops';
-import { Stack } from './payments';
-import * as scriptNumber from './script_number';
-import * as scriptSignature from './script_signature';
-export { OPS };
-export declare function isPushOnly(value: Stack): boolean;
-export declare function compile(chunks: Buffer | Stack): Buffer;
-export declare function decompile(buffer: Buffer | Array): Array | null;
-export declare function toASM(chunks: Buffer | Array): string;
-export declare function fromASM(asm: string): Buffer;
-export declare function toStack(chunks: Buffer | Array): Buffer[];
-export declare function isCanonicalPubKey(buffer: Buffer): boolean;
-export declare function isDefinedHashType(hashType: number): boolean;
-export declare function isCanonicalScriptSignature(buffer: Buffer): boolean;
-export declare const number: typeof scriptNumber;
-export declare const signature: typeof scriptSignature;
diff --git a/src/script.js b/src/script.js
deleted file mode 100644
index 5e5e17ba1..000000000
--- a/src/script.js
+++ /dev/null
@@ -1,182 +0,0 @@
-'use strict';
-Object.defineProperty(exports, '__esModule', { value: true });
-exports.signature = exports.number = exports.isCanonicalScriptSignature = exports.isDefinedHashType = exports.isCanonicalPubKey = exports.toStack = exports.fromASM = exports.toASM = exports.decompile = exports.compile = exports.isPushOnly = exports.OPS = void 0;
-const bip66 = require('./bip66');
-const ops_1 = require('./ops');
-Object.defineProperty(exports, 'OPS', {
- enumerable: true,
- get: function() {
- return ops_1.OPS;
- },
-});
-const pushdata = require('./push_data');
-const scriptNumber = require('./script_number');
-const scriptSignature = require('./script_signature');
-const types = require('./types');
-const { typeforce } = types;
-const OP_INT_BASE = ops_1.OPS.OP_RESERVED; // OP_1 - 1
-function isOPInt(value) {
- return (
- types.Number(value) &&
- (value === ops_1.OPS.OP_0 ||
- (value >= ops_1.OPS.OP_1 && value <= ops_1.OPS.OP_16) ||
- value === ops_1.OPS.OP_1NEGATE)
- );
-}
-function isPushOnlyChunk(value) {
- return types.Buffer(value) || isOPInt(value);
-}
-function isPushOnly(value) {
- return types.Array(value) && value.every(isPushOnlyChunk);
-}
-exports.isPushOnly = isPushOnly;
-function asMinimalOP(buffer) {
- if (buffer.length === 0) return ops_1.OPS.OP_0;
- if (buffer.length !== 1) return;
- if (buffer[0] >= 1 && buffer[0] <= 16) return OP_INT_BASE + buffer[0];
- if (buffer[0] === 0x81) return ops_1.OPS.OP_1NEGATE;
-}
-function chunksIsBuffer(buf) {
- return Buffer.isBuffer(buf);
-}
-function chunksIsArray(buf) {
- return types.Array(buf);
-}
-function singleChunkIsBuffer(buf) {
- return Buffer.isBuffer(buf);
-}
-function compile(chunks) {
- // TODO: remove me
- if (chunksIsBuffer(chunks)) return chunks;
- typeforce(types.Array, chunks);
- const bufferSize = chunks.reduce((accum, chunk) => {
- // data chunk
- if (singleChunkIsBuffer(chunk)) {
- // adhere to BIP62.3, minimal push policy
- if (chunk.length === 1 && asMinimalOP(chunk) !== undefined) {
- return accum + 1;
- }
- return accum + pushdata.encodingLength(chunk.length) + chunk.length;
- }
- // opcode
- return accum + 1;
- }, 0.0);
- const buffer = Buffer.allocUnsafe(bufferSize);
- let offset = 0;
- chunks.forEach(chunk => {
- // data chunk
- if (singleChunkIsBuffer(chunk)) {
- // adhere to BIP62.3, minimal push policy
- const opcode = asMinimalOP(chunk);
- if (opcode !== undefined) {
- buffer.writeUInt8(opcode, offset);
- offset += 1;
- return;
- }
- offset += pushdata.encode(buffer, chunk.length, offset);
- chunk.copy(buffer, offset);
- offset += chunk.length;
- // opcode
- } else {
- buffer.writeUInt8(chunk, offset);
- offset += 1;
- }
- });
- if (offset !== buffer.length) throw new Error('Could not decode chunks');
- return buffer;
-}
-exports.compile = compile;
-function decompile(buffer) {
- // TODO: remove me
- if (chunksIsArray(buffer)) return buffer;
- typeforce(types.Buffer, buffer);
- const chunks = [];
- let i = 0;
- while (i < buffer.length) {
- const opcode = buffer[i];
- // data chunk
- if (opcode > ops_1.OPS.OP_0 && opcode <= ops_1.OPS.OP_PUSHDATA4) {
- const d = pushdata.decode(buffer, i);
- // did reading a pushDataInt fail?
- if (d === null) return null;
- i += d.size;
- // attempt to read too much data?
- if (i + d.number > buffer.length) return null;
- const data = buffer.slice(i, i + d.number);
- i += d.number;
- // decompile minimally
- const op = asMinimalOP(data);
- if (op !== undefined) {
- chunks.push(op);
- } else {
- chunks.push(data);
- }
- // opcode
- } else {
- chunks.push(opcode);
- i += 1;
- }
- }
- return chunks;
-}
-exports.decompile = decompile;
-function toASM(chunks) {
- if (chunksIsBuffer(chunks)) {
- chunks = decompile(chunks);
- }
- return chunks
- .map(chunk => {
- // data?
- if (singleChunkIsBuffer(chunk)) {
- const op = asMinimalOP(chunk);
- if (op === undefined) return chunk.toString('hex');
- chunk = op;
- }
- // opcode!
- return ops_1.REVERSE_OPS[chunk];
- })
- .join(' ');
-}
-exports.toASM = toASM;
-function fromASM(asm) {
- typeforce(types.String, asm);
- return compile(
- asm.split(' ').map(chunkStr => {
- // opcode?
- if (ops_1.OPS[chunkStr] !== undefined) return ops_1.OPS[chunkStr];
- typeforce(types.Hex, chunkStr);
- // data!
- return Buffer.from(chunkStr, 'hex');
- }),
- );
-}
-exports.fromASM = fromASM;
-function toStack(chunks) {
- chunks = decompile(chunks);
- typeforce(isPushOnly, chunks);
- return chunks.map(op => {
- if (singleChunkIsBuffer(op)) return op;
- if (op === ops_1.OPS.OP_0) return Buffer.allocUnsafe(0);
- return scriptNumber.encode(op - OP_INT_BASE);
- });
-}
-exports.toStack = toStack;
-function isCanonicalPubKey(buffer) {
- return types.isPoint(buffer);
-}
-exports.isCanonicalPubKey = isCanonicalPubKey;
-function isDefinedHashType(hashType) {
- const hashTypeMod = hashType & ~0x80;
- // return hashTypeMod > SIGHASH_ALL && hashTypeMod < SIGHASH_SINGLE
- return hashTypeMod > 0x00 && hashTypeMod < 0x04;
-}
-exports.isDefinedHashType = isDefinedHashType;
-function isCanonicalScriptSignature(buffer) {
- if (!Buffer.isBuffer(buffer)) return false;
- if (!isDefinedHashType(buffer[buffer.length - 1])) return false;
- return bip66.check(buffer.slice(0, -1));
-}
-exports.isCanonicalScriptSignature = isCanonicalScriptSignature;
-// tslint:disable-next-line variable-name
-exports.number = scriptNumber;
-exports.signature = scriptSignature;
diff --git a/src/script_number.d.ts b/src/script_number.d.ts
deleted file mode 100644
index 015bb8943..000000000
--- a/src/script_number.d.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-///
-export declare function decode(buffer: Buffer, maxLength?: number, minimal?: boolean): number;
-export declare function encode(_number: number): Buffer;
diff --git a/src/script_number.js b/src/script_number.js
deleted file mode 100644
index 8220a1043..000000000
--- a/src/script_number.js
+++ /dev/null
@@ -1,62 +0,0 @@
-'use strict';
-Object.defineProperty(exports, '__esModule', { value: true });
-exports.encode = exports.decode = void 0;
-function decode(buffer, maxLength, minimal) {
- maxLength = maxLength || 4;
- minimal = minimal === undefined ? true : minimal;
- const length = buffer.length;
- if (length === 0) return 0;
- if (length > maxLength) throw new TypeError('Script number overflow');
- if (minimal) {
- if ((buffer[length - 1] & 0x7f) === 0) {
- if (length <= 1 || (buffer[length - 2] & 0x80) === 0)
- throw new Error('Non-minimally encoded script number');
- }
- }
- // 40-bit
- if (length === 5) {
- const a = buffer.readUInt32LE(0);
- const b = buffer.readUInt8(4);
- if (b & 0x80) return -((b & ~0x80) * 0x100000000 + a);
- return b * 0x100000000 + a;
- }
- // 32-bit / 24-bit / 16-bit / 8-bit
- let result = 0;
- for (let i = 0; i < length; ++i) {
- result |= buffer[i] << (8 * i);
- }
- if (buffer[length - 1] & 0x80)
- return -(result & ~(0x80 << (8 * (length - 1))));
- return result;
-}
-exports.decode = decode;
-function scriptNumSize(i) {
- return i > 0x7fffffff
- ? 5
- : i > 0x7fffff
- ? 4
- : i > 0x7fff
- ? 3
- : i > 0x7f
- ? 2
- : i > 0x00
- ? 1
- : 0;
-}
-function encode(_number) {
- let value = Math.abs(_number);
- const size = scriptNumSize(value);
- const buffer = Buffer.allocUnsafe(size);
- const negative = _number < 0;
- for (let i = 0; i < size; ++i) {
- buffer.writeUInt8(value & 0xff, i);
- value >>= 8;
- }
- if (buffer[size - 1] & 0x80) {
- buffer.writeUInt8(negative ? 0x80 : 0x00, size - 1);
- } else if (negative) {
- buffer[size - 1] |= 0x80;
- }
- return buffer;
-}
-exports.encode = encode;
diff --git a/src/script_signature.d.ts b/src/script_signature.d.ts
deleted file mode 100644
index 2057dd99f..000000000
--- a/src/script_signature.d.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-///
-interface ScriptSignature {
- signature: Buffer;
- hashType: number;
-}
-export declare function decode(buffer: Buffer): ScriptSignature;
-export declare function encode(signature: Buffer, hashType: number): Buffer;
-export {};
diff --git a/src/script_signature.js b/src/script_signature.js
deleted file mode 100644
index 638e5f299..000000000
--- a/src/script_signature.js
+++ /dev/null
@@ -1,53 +0,0 @@
-'use strict';
-Object.defineProperty(exports, '__esModule', { value: true });
-exports.encode = exports.decode = void 0;
-const bip66 = require('./bip66');
-const types = require('./types');
-const { typeforce } = types;
-const ZERO = Buffer.alloc(1, 0);
-function toDER(x) {
- let i = 0;
- while (x[i] === 0) ++i;
- if (i === x.length) return ZERO;
- x = x.slice(i);
- if (x[0] & 0x80) return Buffer.concat([ZERO, x], 1 + x.length);
- return x;
-}
-function fromDER(x) {
- if (x[0] === 0x00) x = x.slice(1);
- const buffer = Buffer.alloc(32, 0);
- const bstart = Math.max(0, 32 - x.length);
- x.copy(buffer, bstart);
- return buffer;
-}
-// BIP62: 1 byte hashType flag (only 0x01, 0x02, 0x03, 0x81, 0x82 and 0x83 are allowed)
-function decode(buffer) {
- const hashType = buffer.readUInt8(buffer.length - 1);
- const hashTypeMod = hashType & ~0x80;
- if (hashTypeMod <= 0 || hashTypeMod >= 4)
- throw new Error('Invalid hashType ' + hashType);
- const decoded = bip66.decode(buffer.slice(0, -1));
- const r = fromDER(decoded.r);
- const s = fromDER(decoded.s);
- const signature = Buffer.concat([r, s], 64);
- return { signature, hashType };
-}
-exports.decode = decode;
-function encode(signature, hashType) {
- typeforce(
- {
- signature: types.BufferN(64),
- hashType: types.UInt8,
- },
- { signature, hashType },
- );
- const hashTypeMod = hashType & ~0x80;
- if (hashTypeMod <= 0 || hashTypeMod >= 4)
- throw new Error('Invalid hashType ' + hashType);
- const hashTypeBuffer = Buffer.allocUnsafe(1);
- hashTypeBuffer.writeUInt8(hashType, 0);
- const r = toDER(signature.slice(0, 32));
- const s = toDER(signature.slice(32, 64));
- return Buffer.concat([bip66.encode(r, s), hashTypeBuffer]);
-}
-exports.encode = encode;
diff --git a/src/transaction.d.ts b/src/transaction.d.ts
deleted file mode 100644
index 613706b67..000000000
--- a/src/transaction.d.ts
+++ /dev/null
@@ -1,57 +0,0 @@
-///
-export interface Output {
- script: Buffer;
- value: number;
-}
-export interface Input {
- hash: Buffer;
- index: number;
- script: Buffer;
- sequence: number;
- witness: Buffer[];
-}
-export declare class Transaction {
- static readonly DEFAULT_SEQUENCE = 4294967295;
- static readonly SIGHASH_DEFAULT = 0;
- static readonly SIGHASH_ALL = 1;
- static readonly SIGHASH_NONE = 2;
- static readonly SIGHASH_SINGLE = 3;
- static readonly SIGHASH_ANYONECANPAY = 128;
- static readonly SIGHASH_OUTPUT_MASK = 3;
- static readonly SIGHASH_INPUT_MASK = 128;
- static readonly ADVANCED_TRANSACTION_MARKER = 0;
- static readonly ADVANCED_TRANSACTION_FLAG = 1;
- static fromBuffer(buffer: Buffer, _NO_STRICT?: boolean): Transaction;
- static fromHex(hex: string): Transaction;
- static isCoinbaseHash(buffer: Buffer): boolean;
- version: number;
- locktime: number;
- ins: Input[];
- outs: Output[];
- isCoinbase(): boolean;
- addInput(hash: Buffer, index: number, sequence?: number, scriptSig?: Buffer): number;
- addOutput(scriptPubKey: Buffer, value: number): number;
- hasWitnesses(): boolean;
- weight(): number;
- virtualSize(): number;
- byteLength(_ALLOW_WITNESS?: boolean): number;
- clone(): Transaction;
- /**
- * Hash transaction for signing a specific input.
- *
- * Bitcoin uses a different hash for each signed transaction input.
- * This method copies the transaction, makes the necessary changes based on the
- * hashType, and then hashes the result.
- * This hash can then be used to sign the provided transaction input.
- */
- hashForSignature(inIndex: number, prevOutScript: Buffer, hashType: number): Buffer;
- hashForWitnessV1(inIndex: number, prevOutScripts: Buffer[], values: number[], hashType: number, leafHash?: Buffer, annex?: Buffer): Buffer;
- hashForWitnessV0(inIndex: number, prevOutScript: Buffer, value: number, hashType: number): Buffer;
- getHash(forWitness?: boolean): Buffer;
- getId(): string;
- toBuffer(buffer?: Buffer, initialOffset?: number): Buffer;
- toHex(): string;
- setInputScript(index: number, scriptSig: Buffer): void;
- setWitness(index: number, witness: Buffer[]): void;
- private __toBuffer;
-}
diff --git a/src/types.d.ts b/src/types.d.ts
deleted file mode 100644
index 5a8505d34..000000000
--- a/src/types.d.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-///
-export declare const typeforce: any;
-export declare function isPoint(p: Buffer | number | undefined | null): boolean;
-export declare function UInt31(value: number): boolean;
-export declare function BIP32Path(value: string): boolean;
-export declare namespace BIP32Path {
- var toJSON: () => string;
-}
-export declare function Signer(obj: any): boolean;
-export declare function Satoshi(value: number): boolean;
-export declare const ECPoint: any;
-export declare const Network: any;
-export declare const Buffer256bit: any;
-export declare const Hash160bit: any;
-export declare const Hash256bit: any;
-export declare const Number: any;
-export declare const Array: any;
-export declare const Boolean: any;
-export declare const String: any;
-export declare const Buffer: any;
-export declare const Hex: any;
-export declare const maybe: any;
-export declare const tuple: any;
-export declare const UInt8: any;
-export declare const UInt32: any;
-export declare const Function: any;
-export declare const BufferN: any;
-export declare const Null: any;
-export declare const oneOf: any;
diff --git a/src/types.js b/src/types.js
deleted file mode 100644
index a6d1efa16..000000000
--- a/src/types.js
+++ /dev/null
@@ -1,87 +0,0 @@
-'use strict';
-Object.defineProperty(exports, '__esModule', { value: true });
-exports.oneOf = exports.Null = exports.BufferN = exports.Function = exports.UInt32 = exports.UInt8 = exports.tuple = exports.maybe = exports.Hex = exports.Buffer = exports.String = exports.Boolean = exports.Array = exports.Number = exports.Hash256bit = exports.Hash160bit = exports.Buffer256bit = exports.Network = exports.ECPoint = exports.Satoshi = exports.Signer = exports.BIP32Path = exports.UInt31 = exports.isPoint = exports.typeforce = void 0;
-const buffer_1 = require('buffer');
-exports.typeforce = require('typeforce');
-const ZERO32 = buffer_1.Buffer.alloc(32, 0);
-const EC_P = buffer_1.Buffer.from(
- 'fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f',
- 'hex',
-);
-function isPoint(p) {
- if (!buffer_1.Buffer.isBuffer(p)) return false;
- if (p.length < 33) return false;
- const t = p[0];
- const x = p.slice(1, 33);
- if (x.compare(ZERO32) === 0) return false;
- if (x.compare(EC_P) >= 0) return false;
- if ((t === 0x02 || t === 0x03) && p.length === 33) {
- return true;
- }
- const y = p.slice(33);
- if (y.compare(ZERO32) === 0) return false;
- if (y.compare(EC_P) >= 0) return false;
- if (t === 0x04 && p.length === 65) return true;
- return false;
-}
-exports.isPoint = isPoint;
-const UINT31_MAX = Math.pow(2, 31) - 1;
-function UInt31(value) {
- return exports.typeforce.UInt32(value) && value <= UINT31_MAX;
-}
-exports.UInt31 = UInt31;
-function BIP32Path(value) {
- return (
- exports.typeforce.String(value) && !!value.match(/^(m\/)?(\d+'?\/)*\d+'?$/)
- );
-}
-exports.BIP32Path = BIP32Path;
-BIP32Path.toJSON = () => {
- return 'BIP32 derivation path';
-};
-function Signer(obj) {
- return (
- (exports.typeforce.Buffer(obj.publicKey) ||
- typeof obj.getPublicKey === 'function') &&
- typeof obj.sign === 'function'
- );
-}
-exports.Signer = Signer;
-const SATOSHI_MAX = 21 * 1e14;
-function Satoshi(value) {
- return exports.typeforce.UInt53(value) && value <= SATOSHI_MAX;
-}
-exports.Satoshi = Satoshi;
-// external dependent types
-exports.ECPoint = exports.typeforce.quacksLike('Point');
-// exposed, external API
-exports.Network = exports.typeforce.compile({
- messagePrefix: exports.typeforce.oneOf(
- exports.typeforce.Buffer,
- exports.typeforce.String,
- ),
- bip32: {
- public: exports.typeforce.UInt32,
- private: exports.typeforce.UInt32,
- },
- pubKeyHash: exports.typeforce.UInt8,
- scriptHash: exports.typeforce.UInt8,
- wif: exports.typeforce.UInt8,
-});
-exports.Buffer256bit = exports.typeforce.BufferN(32);
-exports.Hash160bit = exports.typeforce.BufferN(20);
-exports.Hash256bit = exports.typeforce.BufferN(32);
-exports.Number = exports.typeforce.Number; // tslint:disable-line variable-name
-exports.Array = exports.typeforce.Array;
-exports.Boolean = exports.typeforce.Boolean; // tslint:disable-line variable-name
-exports.String = exports.typeforce.String; // tslint:disable-line variable-name
-exports.Buffer = exports.typeforce.Buffer;
-exports.Hex = exports.typeforce.Hex;
-exports.maybe = exports.typeforce.maybe;
-exports.tuple = exports.typeforce.tuple;
-exports.UInt8 = exports.typeforce.UInt8;
-exports.UInt32 = exports.typeforce.UInt32;
-exports.Function = exports.typeforce.Function;
-exports.BufferN = exports.typeforce.BufferN;
-exports.Null = exports.typeforce.Null;
-exports.oneOf = exports.typeforce.oneOf;
diff --git a/test/address.spec.ts b/test/address.spec.ts
index b1304c323..8ef401791 100644
--- a/test/address.spec.ts
+++ b/test/address.spec.ts
@@ -1,24 +1,28 @@
import * as assert from 'assert';
import { describe, it } from 'mocha';
-import * as baddress from '../src/address';
-import * as bscript from '../src/script';
-import * as fixtures from './fixtures/address.json';
-
-const NETWORKS = Object.assign(
- {
- litecoin: {
- messagePrefix: '\x19Litecoin Signed Message:\n',
- bip32: {
- public: 0x019da462,
- private: 0x019d9cfe,
- },
- pubKeyHash: 0x30,
- scriptHash: 0x32,
- wif: 0xb0,
+import * as ecc from 'tiny-secp256k1';
+import { address as baddress } from 'bitcoinjs-lib';
+import { script as bscript } from 'bitcoinjs-lib';
+import fixtures from './fixtures/address.json';
+import * as tools from 'uint8array-tools';
+import { networks } from 'bitcoinjs-lib';
+
+import { initEccLib } from 'bitcoinjs-lib';
+
+const NETWORKS: Record = {
+ ...networks,
+ litecoin: {
+ messagePrefix: '\x19Litecoin Signed Message:\n',
+ bech32: 'ltc',
+ bip32: {
+ public: 0x019da462,
+ private: 0x019d9cfe,
},
- },
- require('../src/networks'),
-);
+ pubKeyHash: 0x30,
+ scriptHash: 0x32,
+ wif: 0xb0,
+ } as networks.Network,
+};
describe('address', () => {
describe('fromBase58Check', () => {
@@ -29,15 +33,18 @@ describe('address', () => {
const decode = baddress.fromBase58Check(f.base58check);
assert.strictEqual(decode.version, f.version);
- assert.strictEqual(decode.hash.toString('hex'), f.hash);
+ assert.strictEqual(tools.toHex(decode.hash), f.hash);
});
});
fixtures.invalid.fromBase58Check.forEach(f => {
it('throws on ' + f.exception, () => {
- assert.throws(() => {
- baddress.fromBase58Check(f.address);
- }, new RegExp(f.address + ' ' + f.exception));
+ assert.throws(
+ () => {
+ baddress.fromBase58Check(f.address);
+ },
+ new RegExp(f.address + ' ' + f.exception),
+ );
});
});
});
@@ -51,7 +58,7 @@ describe('address', () => {
assert.strictEqual(actual.version, f.version);
assert.strictEqual(actual.prefix, NETWORKS[f.network].bech32);
- assert.strictEqual(actual.data.toString('hex'), f.data);
+ assert.strictEqual(tools.toHex(actual.data), f.data);
});
});
@@ -65,6 +72,7 @@ describe('address', () => {
});
describe('fromOutputScript', () => {
+ initEccLib(ecc);
fixtures.standard.forEach(f => {
it('encodes ' + f.script.slice(0, 30) + '... (' + f.network + ')', () => {
const script = bscript.fromASM(f.script);
@@ -79,7 +87,7 @@ describe('address', () => {
const script = bscript.fromASM(f.script);
assert.throws(() => {
- baddress.fromOutputScript(script);
+ baddress.fromOutputScript(script, undefined);
}, new RegExp(f.exception));
});
});
@@ -138,10 +146,11 @@ describe('address', () => {
});
fixtures.invalid.toOutputScript.forEach(f => {
- it('throws when ' + f.exception, () => {
+ it('throws when ' + (f.exception || f.paymentException), () => {
+ const exception = f.paymentException || `${f.address} ${f.exception}`;
assert.throws(() => {
baddress.toOutputScript(f.address, f.network as any);
- }, new RegExp(f.address + ' ' + f.exception));
+ }, new RegExp(exception));
});
});
});
diff --git a/test/bip371.spec.ts b/test/bip371.spec.ts
new file mode 100644
index 000000000..67656fd8a
--- /dev/null
+++ b/test/bip371.spec.ts
@@ -0,0 +1,23 @@
+import { toXOnly } from 'bitcoinjs-lib';
+import * as assert from 'assert';
+
+describe('toXOnly', () => {
+ it('should return the input if the pubKey length is 32', () => {
+ const pubKey = new Uint8Array(32).fill(1); // Example 32-byte public key
+ const result = toXOnly(pubKey);
+ assert.strictEqual(result, pubKey); // Expect the same array (reference equality)
+ });
+
+ it('should return the sliced key if the pubKey length is greater than 32', () => {
+ const pubKey = new Uint8Array(33).fill(1);
+ pubKey[0] = 0; // Add a leading byte
+ const result = toXOnly(pubKey);
+ assert.deepStrictEqual(result, pubKey.slice(1, 33)); // Expect the sliced array
+ });
+
+ it('should return the key if the pubKey length is less than 32', () => {
+ const pubKey = new Uint8Array(31).fill(1); // Example invalid public key
+ const result = toXOnly(pubKey);
+ assert.deepStrictEqual(result, pubKey.slice(1, 33)); // Expect the sliced array
+ });
+});
diff --git a/test/bitcoin.core.spec.ts b/test/bitcoin.core.spec.ts
index 9040416bb..1c1b50787 100644
--- a/test/bitcoin.core.spec.ts
+++ b/test/bitcoin.core.spec.ts
@@ -1,15 +1,16 @@
import * as assert from 'assert';
-import * as base58 from 'bs58';
+import base58 from 'bs58';
import { describe, it } from 'mocha';
-import * as bitcoin from '..';
-import * as base58EncodeDecode from './fixtures/core/base58_encode_decode.json';
-import * as base58KeysInvalid from './fixtures/core/base58_keys_invalid.json';
-import * as base58KeysValid from './fixtures/core/base58_keys_valid.json';
-import * as blocksValid from './fixtures/core/blocks.json';
-import * as sigCanonical from './fixtures/core/sig_canonical.json';
-import * as sigNoncanonical from './fixtures/core/sig_noncanonical.json';
-import * as sigHash from './fixtures/core/sighash.json';
-import * as txValid from './fixtures/core/tx_valid.json';
+import * as bitcoin from 'bitcoinjs-lib';
+import base58EncodeDecode from './fixtures/core/base58_encode_decode.json';
+import base58KeysInvalid from './fixtures/core/base58_keys_invalid.json';
+import base58KeysValid from './fixtures/core/base58_keys_valid.json';
+import blocksValid from './fixtures/core/blocks.json';
+import sigCanonical from './fixtures/core/sig_canonical.json';
+import sigNoncanonical from './fixtures/core/sig_noncanonical.json';
+import sigHash from './fixtures/core/sighash.json';
+import txValid from './fixtures/core/tx_valid.json';
+import * as tools from 'uint8array-tools';
describe('Bitcoin-core', () => {
// base58EncodeDecode
@@ -20,7 +21,7 @@ describe('Bitcoin-core', () => {
it('can decode ' + fb58, () => {
const buffer = base58.decode(fb58);
- const actual = buffer.toString('hex');
+ const actual = tools.toHex(buffer);
assert.strictEqual(actual, fhex);
});
@@ -119,7 +120,7 @@ describe('Bitcoin-core', () => {
const prevOutHash = Buffer.from(input[0] as string, 'hex').reverse();
const prevOutIndex = input[1];
- assert.deepStrictEqual(txIn.hash, prevOutHash);
+ assert.deepStrictEqual(Buffer.from(txIn.hash), prevOutHash);
// we read UInt32, not Int32
assert.strictEqual(txIn.index & 0xffffffff, prevOutIndex);
@@ -140,7 +141,7 @@ describe('Bitcoin-core', () => {
const hashType = f[3] as number;
const expectedHash = f[4];
- const hashTypes = [];
+ const hashTypes: string[] = [];
if ((hashType & 0x1f) === bitcoin.Transaction.SIGHASH_NONE)
hashTypes.push('SIGHASH_NONE');
else if ((hashType & 0x1f) === bitcoin.Transaction.SIGHASH_SINGLE)
@@ -160,7 +161,7 @@ describe('Bitcoin-core', () => {
const script = Buffer.from(scriptHex, 'hex');
const scriptChunks = bitcoin.script.decompile(script);
assert.strictEqual(
- bitcoin.script.compile(scriptChunks!).toString('hex'),
+ tools.toHex(bitcoin.script.compile(scriptChunks!)),
scriptHex,
);
@@ -168,7 +169,7 @@ describe('Bitcoin-core', () => {
// reverse because test data is reversed
assert.strictEqual(
- (hash.reverse() as Buffer).toString('hex'),
+ tools.toHex(hash.reverse() as Buffer),
expectedHash,
);
@@ -176,7 +177,7 @@ describe('Bitcoin-core', () => {
transaction.hashForWitnessV0(
inIndex,
script,
- 0,
+ BigInt(0),
// convert to UInt32
hashType < 0 ? 0x100000000 + hashType : hashType,
),
@@ -197,7 +198,7 @@ describe('Bitcoin-core', () => {
parsed.hashType,
);
- assert.strictEqual(actual.toString('hex'), hex);
+ assert.strictEqual(tools.toHex(actual), hex);
});
});
diff --git a/test/block.spec.ts b/test/block.spec.ts
index e93420c2e..7cd123193 100644
--- a/test/block.spec.ts
+++ b/test/block.spec.ts
@@ -1,8 +1,9 @@
import * as assert from 'assert';
import { beforeEach, describe, it } from 'mocha';
-import { Block } from '..';
+import { Block } from 'bitcoinjs-lib';
+import * as tools from 'uint8array-tools';
-import * as fixtures from './fixtures/block.json';
+import fixtures from './fixtures/block.json';
describe('Block', () => {
describe('version', () => {
@@ -23,7 +24,7 @@ describe('Block', () => {
const bits = parseInt(f.bits, 16);
assert.strictEqual(
- Block.calculateTarget(bits).toString('hex'),
+ tools.toHex(Block.calculateTarget(bits)),
f.expected,
);
});
@@ -36,13 +37,10 @@ describe('Block', () => {
const block = Block.fromHex(f.hex);
assert.strictEqual(block.version, f.version);
- assert.strictEqual(block.prevHash!.toString('hex'), f.prevHash);
- assert.strictEqual(block.merkleRoot!.toString('hex'), f.merkleRoot);
+ assert.strictEqual(tools.toHex(block.prevHash!), f.prevHash);
+ assert.strictEqual(tools.toHex(block.merkleRoot!), f.merkleRoot);
if (block.witnessCommit) {
- assert.strictEqual(
- block.witnessCommit.toString('hex'),
- f.witnessCommit,
- );
+ assert.strictEqual(tools.toHex(block.witnessCommit), f.witnessCommit);
}
assert.strictEqual(block.timestamp, f.timestamp);
assert.strictEqual(block.bits, f.bits);
@@ -89,7 +87,7 @@ describe('Block', () => {
});
it('returns ' + f.id + ' for ' + f.description, () => {
- assert.strictEqual(block.getHash().toString('hex'), f.hash);
+ assert.strictEqual(tools.toHex(block.getHash()), f.hash);
assert.strictEqual(block.getId(), f.id);
});
});
@@ -129,7 +127,7 @@ describe('Block', () => {
it('returns ' + f.merkleRoot + ' for ' + f.id, () => {
assert.strictEqual(
- Block.calculateMerkleRoot(block.transactions!).toString('hex'),
+ tools.toHex(Block.calculateMerkleRoot(block.transactions!)),
f.merkleRoot,
);
});
@@ -137,9 +135,7 @@ describe('Block', () => {
if (f.witnessCommit) {
it('returns witness commit ' + f.witnessCommit + ' for ' + f.id, () => {
assert.strictEqual(
- Block.calculateMerkleRoot(block.transactions!, true).toString(
- 'hex',
- ),
+ tools.toHex(Block.calculateMerkleRoot(block.transactions!, true)),
f.witnessCommit,
);
});
diff --git a/test/bufferutils.spec.ts b/test/bufferutils.spec.ts
index 0f1f1a901..e3e17f1a1 100644
--- a/test/bufferutils.spec.ts
+++ b/test/bufferutils.spec.ts
@@ -1,68 +1,24 @@
import * as assert from 'assert';
import { describe, it } from 'mocha';
-import * as bufferutils from '../src/bufferutils';
-import { BufferReader, BufferWriter } from '../src/bufferutils';
+import { BufferReader, BufferWriter } from 'bitcoinjs-lib/src/bufferutils';
-import * as fixtures from './fixtures/bufferutils.json';
-const varuint = require('varuint-bitcoin');
+import * as varuint from 'varuint-bitcoin';
describe('bufferutils', () => {
function concatToBuffer(values: number[][]): Buffer {
return Buffer.concat(values.map(data => Buffer.from(data)));
}
- describe('readUInt64LE', () => {
- fixtures.valid.forEach(f => {
- it('decodes ' + f.hex, () => {
- const buffer = Buffer.from(f.hex, 'hex');
- const num = bufferutils.readUInt64LE(buffer, 0);
-
- assert.strictEqual(num, f.dec);
- });
- });
-
- fixtures.invalid.readUInt64LE.forEach(f => {
- it('throws on ' + f.description, () => {
- const buffer = Buffer.from(f.hex, 'hex');
-
- assert.throws(() => {
- bufferutils.readUInt64LE(buffer, 0);
- }, new RegExp(f.exception));
- });
- });
- });
-
- describe('writeUInt64LE', () => {
- fixtures.valid.forEach(f => {
- it('encodes ' + f.dec, () => {
- const buffer = Buffer.alloc(8, 0);
-
- bufferutils.writeUInt64LE(buffer, f.dec, 0);
- assert.strictEqual(buffer.toString('hex'), f.hex);
- });
- });
-
- fixtures.invalid.writeUInt64LE.forEach(f => {
- it('throws on ' + f.description, () => {
- const buffer = Buffer.alloc(8, 0);
-
- assert.throws(() => {
- bufferutils.writeUInt64LE(buffer, f.dec, 0);
- }, new RegExp(f.exception));
- });
- });
- });
-
describe('BufferWriter', () => {
function testBuffer(
bufferWriter: BufferWriter,
- expectedBuffer: Buffer,
+ expectedBuffer: Uint8Array,
expectedOffset: number = expectedBuffer.length,
): void {
assert.strictEqual(bufferWriter.offset, expectedOffset);
assert.deepStrictEqual(
- bufferWriter.buffer.slice(0, expectedOffset),
- expectedBuffer.slice(0, expectedOffset),
+ Buffer.from(bufferWriter.buffer.slice(0, expectedOffset)),
+ Buffer.from(expectedBuffer.slice(0, expectedOffset)),
);
}
@@ -299,24 +255,24 @@ describe('bufferutils', () => {
describe('BufferReader', () => {
function testValue(
bufferReader: BufferReader,
- value: Buffer | number,
- expectedValue: Buffer | number,
+ value: Uint8Array | bigint | number,
+ expectedValue: Uint8Array | bigint | number,
expectedOffset: number = Buffer.isBuffer(expectedValue)
? expectedValue.length
: 0,
): void {
assert.strictEqual(bufferReader.offset, expectedOffset);
- if (Buffer.isBuffer(expectedValue)) {
+ if (expectedValue instanceof Buffer) {
assert.deepStrictEqual(
- (value as Buffer).slice(0, expectedOffset),
- expectedValue.slice(0, expectedOffset),
+ Buffer.from(value as Buffer).slice(0, expectedOffset),
+ Buffer.from(expectedValue).slice(0, expectedOffset),
);
} else {
- assert.strictEqual(value as number, expectedValue);
+ assert.strictEqual(value, expectedValue);
}
}
- it('readUint8', () => {
+ it('readUInt8', () => {
const values = [0, 1, 0xfe, 0xff];
const buffer = Buffer.from([0, 1, 0xfe, 0xff]);
const bufferReader = new BufferReader(buffer);
@@ -369,23 +325,25 @@ describe('bufferutils', () => {
});
});
- it('readUInt64', () => {
+ it('readInt64', () => {
const values = [
- 0,
- 1,
- Math.pow(2, 32),
- Number.MAX_SAFE_INTEGER /* 2^53 - 1 */,
+ 0n,
+ 1n,
+ BigInt(Math.pow(2, 32)),
+ BigInt(Number.MAX_SAFE_INTEGER) /* 2^53 - 1 */,
+ (BigInt(1) << 63n) - 1n,
];
const buffer = concatToBuffer([
[0, 0, 0, 0, 0, 0, 0, 0],
[1, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 0, 0],
[0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x00],
+ [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f],
]);
const bufferReader = new BufferReader(buffer);
- values.forEach((value: number) => {
+ values.forEach((value: bigint) => {
const expectedOffset = bufferReader.offset + 8;
- const val = bufferReader.readUInt64();
+ const val = bufferReader.readInt64();
testValue(bufferReader, val, value, expectedOffset);
});
});
@@ -427,7 +385,7 @@ describe('bufferutils', () => {
values.forEach((value: number) => {
const expectedOffset =
bufferReader.offset + varuint.encodingLength(value);
- const val = bufferReader.readVarInt();
+ const val = Number(bufferReader.readVarInt());
testValue(bufferReader, val, value, expectedOffset);
});
});
diff --git a/test/crypto.spec.ts b/test/crypto.spec.ts
index 0482ec9ec..d2236f35b 100644
--- a/test/crypto.spec.ts
+++ b/test/crypto.spec.ts
@@ -1,7 +1,11 @@
import * as assert from 'assert';
import { describe, it } from 'mocha';
-import { crypto as bcrypto, TaggedHashPrefix } from '..';
-import * as fixtures from './fixtures/crypto.json';
+import { crypto as bcrypto } from 'bitcoinjs-lib';
+import type { TaggedHashPrefix } from 'bitcoinjs-lib';
+import fixtures from './fixtures/crypto.json';
+import * as tools from 'uint8array-tools';
+import { TAGS, TAGGED_HASH_PREFIXES } from 'bitcoinjs-lib/src/crypto';
+import { sha256 } from '@noble/hashes/sha256';
describe('crypto', () => {
['hash160', 'hash256', 'ripemd160', 'sha1', 'sha256'].forEach(algorithm => {
@@ -12,9 +16,9 @@ describe('crypto', () => {
it('returns ' + expected + ' for ' + f.hex, () => {
const data = Buffer.from(f.hex, 'hex');
- const actual = fn(data).toString('hex');
+ const actual = fn(data);
- assert.strictEqual(actual, expected);
+ assert.strictEqual(tools.toHex(actual), expected);
});
});
});
@@ -23,11 +27,26 @@ describe('crypto', () => {
describe('taggedHash', () => {
fixtures.taggedHash.forEach(f => {
const bytes = Buffer.from(f.hex, 'hex');
- const expected = Buffer.from(f.result, 'hex');
+ const expected = f.result;
it(`returns ${f.result} for taggedHash "${f.tag}" of ${f.hex}`, () => {
const actual = bcrypto.taggedHash(f.tag as TaggedHashPrefix, bytes);
- assert.strictEqual(actual.toString('hex'), expected.toString('hex'));
+ assert.strictEqual(tools.toHex(actual), expected);
});
});
});
+
+ describe('TAGGED_HASH_PREFIXES', () => {
+ const taggedHashPrefixes = Object.fromEntries(
+ TAGS.map((tag: TaggedHashPrefix) => {
+ const tagHash = sha256(Buffer.from(tag));
+ return [tag, tools.concat([tagHash, tagHash])];
+ }),
+ );
+ it('stored the result of operation', () => {
+ assert.strictEqual(
+ JSON.stringify(TAGGED_HASH_PREFIXES),
+ JSON.stringify(taggedHashPrefixes),
+ );
+ });
+ });
});
diff --git a/test/ecc_lib.spec.ts b/test/ecc_lib.spec.ts
new file mode 100644
index 000000000..fcc1d5f6e
--- /dev/null
+++ b/test/ecc_lib.spec.ts
@@ -0,0 +1,22 @@
+import { initEccLib } from 'bitcoinjs-lib';
+import { describe, test } from 'mocha';
+import * as assert from 'assert';
+
+describe(`initEccLib`, () => {
+ beforeEach(() => {
+ initEccLib(undefined);
+ });
+
+ test('initEccLib should fail when invalid', () => {
+ assert.throws(() => {
+ initEccLib({ isXOnlyPoint: () => false } as any);
+ }, 'Error: ecc library invalid');
+ });
+
+ test('initEccLib should not fail when DANGER_DO_NOT_VERIFY_ECCLIB = true', () => {
+ initEccLib({ isXOnlyPoint: () => false } as any, {
+ DANGER_DO_NOT_VERIFY_ECCLIB: true,
+ });
+ assert.ok('it does not fail, verification is excluded');
+ });
+});
diff --git a/test/fixtures/address.json b/test/fixtures/address.json
index 765ea8aca..0430b7887 100644
--- a/test/fixtures/address.json
+++ b/test/fixtures/address.json
@@ -80,10 +80,10 @@
},
{
"network": "bitcoin",
- "bech32": "bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kt5nd6y",
+ "bech32": "bc1p3efq8ujsj0qr5xvms7mv89p8cz0crqdtuxe9ms6grqgxc9sgsntslthf6w",
"version": 1,
- "data": "751e76e8199196d454941c45d1b3a323f1433bd6751e76e8199196d454941c45d1b3a323f1433bd6",
- "script": "OP_1 751e76e8199196d454941c45d1b3a323f1433bd6751e76e8199196d454941c45d1b3a323f1433bd6"
+ "data": "8e5203f25093c03a199b87b6c39427c09f8181abe1b25dc34818106c160884d7",
+ "script": "OP_1 8e5203f25093c03a199b87b6c39427c09f8181abe1b25dc34818106c160884d7"
},
{
"network": "bitcoin",
@@ -116,10 +116,16 @@
],
"bech32": [
{
- "address": "bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kt5nd6y",
+ "address": "tb1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesrxh6hy",
+ "version": 0,
+ "prefix": "tb",
+ "data": "000000c4a5cad46221b2a187905e5266362b99d5e91c6ce24d165dab93e86433"
+ },
+ {
+ "address": "bc1p3efq8ujsj0qr5xvms7mv89p8cz0crqdtuxe9ms6grqgxc9sgsntslthf6w",
"version": 1,
"prefix": "bc",
- "data": "751e76e8199196d454941c45d1b3a323f1433bd6751e76e8199196d454941c45d1b3a323f1433bd6"
+ "data": "8e5203f25093c03a199b87b6c39427c09f8181abe1b25dc34818106c160884d7"
},
{
"address": "bc1zw508d6qejxtdg4y5r3zarvaryvaxxpcs",
@@ -195,6 +201,19 @@
{
"exception": "has no matching Address",
"script": "OP_0 751e76e8199196d454941c45d1b3a323f1433bd6751e76e8199196d454941c45d1b3a323f1433bd675"
+ },
+ {
+ "exception": "has no matching Address",
+ "script": "OP_1 75"
+ },
+ {
+ "exception": "has no matching Address",
+ "script": "OP_1 751e76e8199196d454941c45d1b3a323f1433bd6751e76e8199196d454941c45d1b3a323f1433bd675"
+ },
+ {
+ "description": "pubkey is not valid x coordinate",
+ "exception": "has no matching Address",
+ "script": "OP_1 fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f"
}
],
"toOutputScript": [
@@ -297,6 +316,14 @@
{
"address": "bc1gmk9yu",
"exception": "has no matching Script"
+ },
+ {
+ "address": "bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kw55h884v",
+ "exception": "has no matching Script"
+ },
+ {
+ "address": "bc1pllllllllllllllllllllllllllllllllllllllllllllallllshsdfvw2y",
+ "paymentException": "TypeError: Invalid pubkey for p2tr"
}
]
}
diff --git a/test/fixtures/p2ms.json b/test/fixtures/p2ms.json
index f84b4d5db..fe9d0d6bb 100644
--- a/test/fixtures/p2ms.json
+++ b/test/fixtures/p2ms.json
@@ -180,6 +180,44 @@
"input": "OP_0 OP_0 300602010102010001",
"witness": []
}
+ },
+ {
+ "description": "output from output (20-of-20 multisig)",
+ "arguments": {
+ "output": "14 0255355ca83c973f1d97ce0e3843c85d78905af16b4dc531bc488e57212d230116 03e3e592638b492e642f8c389f9577b0809d4f73032c4c0f9981cb57cb3eebbe4c 02b0a4e912141c3b1044cdc13f196ff95c916f05f43d04184b7dcefa6977fac24a 02958361ee738c994b5e799c13c964602915eaa847ed7e5a5a3f8c42312cd39a61 0227654f4d0ddea28183970c7532692fadf8dd042e31a51c5936f85487c5a1ec02 02c38b046055858679daf9468ac44c991cce4bf91f9f8f4eab6ea7f9d2041e499f 0335676ec077b748a253dd92a1ca9387533818e511741281ebc96d61eccd86cf39 03165f2a7bbd0789c795f66ca0c383d963fa17bf4289d9faae8b8b8f098b3e669f 032c3263ced2ce21ac62ff8828f67cf12a4cc3cb93edfd432ea4a1cba2d533bae8 0304f4f8a3039ab91a1bbe211a1e16b80b549d9feffb4b83cd9e4d43d7e55964d1 03e3dfe07b7c83bdd7908795f890ba8de2117fc3303d048edd54a72de1183ed737 02ef28db8ca852fd8f871835d31a600728cf1f76744dc6b22c9d36394d146a04e5 03ce600e3f61f8b72de1715654010fa54da7a12d39b8d8cb9969f160888eaa2e0e 02df6d74c70b197cf0fc65216ab5ce25b120338a02049a45907f6e54b2e7c779b8 029f5f53b28673bb834c082f3ccd4e73c1a2099368fe7b8567cb817b7675531e26 023ccd807197e3af4139ad4647a0350bef5829f41651729defac3863e964cd3cb1 02482d77f0fb886bb23d9c431960933499982f6ecaf45f2e279203dbe642aca03b 02312b2cac8fb58150596ce11f7db6a50775d257fb8f9bdd1a3e129eef10ea182c 038c6bd3d819d30aa07cb52a2ca4aaaaf83e63bc9947a9e0230abe5233af1c12dd 038e3b9db1442165010102596f30536020e451b6e645ef1fb0c21cb965f84e3eee 14 OP_CHECKMULTISIG"
+ },
+ "options": {},
+ "expected": {
+ "m": 20,
+ "n": 20,
+ "name": "p2ms(20 of 20)",
+ "output": "14 0255355ca83c973f1d97ce0e3843c85d78905af16b4dc531bc488e57212d230116 03e3e592638b492e642f8c389f9577b0809d4f73032c4c0f9981cb57cb3eebbe4c 02b0a4e912141c3b1044cdc13f196ff95c916f05f43d04184b7dcefa6977fac24a 02958361ee738c994b5e799c13c964602915eaa847ed7e5a5a3f8c42312cd39a61 0227654f4d0ddea28183970c7532692fadf8dd042e31a51c5936f85487c5a1ec02 02c38b046055858679daf9468ac44c991cce4bf91f9f8f4eab6ea7f9d2041e499f 0335676ec077b748a253dd92a1ca9387533818e511741281ebc96d61eccd86cf39 03165f2a7bbd0789c795f66ca0c383d963fa17bf4289d9faae8b8b8f098b3e669f 032c3263ced2ce21ac62ff8828f67cf12a4cc3cb93edfd432ea4a1cba2d533bae8 0304f4f8a3039ab91a1bbe211a1e16b80b549d9feffb4b83cd9e4d43d7e55964d1 03e3dfe07b7c83bdd7908795f890ba8de2117fc3303d048edd54a72de1183ed737 02ef28db8ca852fd8f871835d31a600728cf1f76744dc6b22c9d36394d146a04e5 03ce600e3f61f8b72de1715654010fa54da7a12d39b8d8cb9969f160888eaa2e0e 02df6d74c70b197cf0fc65216ab5ce25b120338a02049a45907f6e54b2e7c779b8 029f5f53b28673bb834c082f3ccd4e73c1a2099368fe7b8567cb817b7675531e26 023ccd807197e3af4139ad4647a0350bef5829f41651729defac3863e964cd3cb1 02482d77f0fb886bb23d9c431960933499982f6ecaf45f2e279203dbe642aca03b 02312b2cac8fb58150596ce11f7db6a50775d257fb8f9bdd1a3e129eef10ea182c 038c6bd3d819d30aa07cb52a2ca4aaaaf83e63bc9947a9e0230abe5233af1c12dd 038e3b9db1442165010102596f30536020e451b6e645ef1fb0c21cb965f84e3eee 14 OP_CHECKMULTISIG",
+ "pubkeys": [
+ "0255355ca83c973f1d97ce0e3843c85d78905af16b4dc531bc488e57212d230116",
+ "03e3e592638b492e642f8c389f9577b0809d4f73032c4c0f9981cb57cb3eebbe4c",
+ "02b0a4e912141c3b1044cdc13f196ff95c916f05f43d04184b7dcefa6977fac24a",
+ "02958361ee738c994b5e799c13c964602915eaa847ed7e5a5a3f8c42312cd39a61",
+ "0227654f4d0ddea28183970c7532692fadf8dd042e31a51c5936f85487c5a1ec02",
+ "02c38b046055858679daf9468ac44c991cce4bf91f9f8f4eab6ea7f9d2041e499f",
+ "0335676ec077b748a253dd92a1ca9387533818e511741281ebc96d61eccd86cf39",
+ "03165f2a7bbd0789c795f66ca0c383d963fa17bf4289d9faae8b8b8f098b3e669f",
+ "032c3263ced2ce21ac62ff8828f67cf12a4cc3cb93edfd432ea4a1cba2d533bae8",
+ "0304f4f8a3039ab91a1bbe211a1e16b80b549d9feffb4b83cd9e4d43d7e55964d1",
+ "03e3dfe07b7c83bdd7908795f890ba8de2117fc3303d048edd54a72de1183ed737",
+ "02ef28db8ca852fd8f871835d31a600728cf1f76744dc6b22c9d36394d146a04e5",
+ "03ce600e3f61f8b72de1715654010fa54da7a12d39b8d8cb9969f160888eaa2e0e",
+ "02df6d74c70b197cf0fc65216ab5ce25b120338a02049a45907f6e54b2e7c779b8",
+ "029f5f53b28673bb834c082f3ccd4e73c1a2099368fe7b8567cb817b7675531e26",
+ "023ccd807197e3af4139ad4647a0350bef5829f41651729defac3863e964cd3cb1",
+ "02482d77f0fb886bb23d9c431960933499982f6ecaf45f2e279203dbe642aca03b",
+ "02312b2cac8fb58150596ce11f7db6a50775d257fb8f9bdd1a3e129eef10ea182c",
+ "038c6bd3d819d30aa07cb52a2ca4aaaaf83e63bc9947a9e0230abe5233af1c12dd",
+ "038e3b9db1442165010102596f30536020e451b6e645ef1fb0c21cb965f84e3eee"
+ ],
+ "signatures": null,
+ "input": null,
+ "witness": null
+ }
}
],
"invalid": [
@@ -225,14 +263,14 @@
},
{
"description": "m is 0",
- "exception": "Output is invalid",
+ "exception": "Invalid opcode: expected OP_1–OP_16, got 0",
"arguments": {
"output": "OP_0 OP_2 OP_CHECKMULTISIG"
}
},
{
"description": "n is 0 (m > n)",
- "exception": "Output is invalid",
+ "exception": "Invalid opcode: expected OP_1–OP_16, got 0",
"arguments": {
"output": "OP_2 OP_0 OP_CHECKMULTISIG"
}
@@ -368,6 +406,13 @@
],
"input": "OP_0 ffffffffffffffff"
}
+ },
+ {
+ "description": "n > 20 (2-of-21 multisig)",
+ "exception": "Output is invalid",
+ "arguments": {
+ "output": "OP_2 020000000000000000000000000000000000000000000000000000000000000001 020000000000000000000000000000000000000000000000000000000000000002 020000000000000000000000000000000000000000000000000000000000000003 020000000000000000000000000000000000000000000000000000000000000004 020000000000000000000000000000000000000000000000000000000000000005 020000000000000000000000000000000000000000000000000000000000000006 020000000000000000000000000000000000000000000000000000000000000007 020000000000000000000000000000000000000000000000000000000000000008 020000000000000000000000000000000000000000000000000000000000000009 02000000000000000000000000000000000000000000000000000000000000000a 02000000000000000000000000000000000000000000000000000000000000000b 02000000000000000000000000000000000000000000000000000000000000000c 02000000000000000000000000000000000000000000000000000000000000000d 02000000000000000000000000000000000000000000000000000000000000000e 02000000000000000000000000000000000000000000000000000000000000000f 020000000000000000000000000000000000000000000000000000000000000010 020000000000000000000000000000000000000000000000000000000000000011 020000000000000000000000000000000000000000000000000000000000000012 020000000000000000000000000000000000000000000000000000000000000013 020000000000000000000000000000000000000000000000000000000000000014 15 OP_CHECKMULTISIG"
+ }
}
],
"dynamic": {
diff --git a/test/fixtures/p2pk.json b/test/fixtures/p2pk.json
index f3982d327..f93ca87a1 100644
--- a/test/fixtures/p2pk.json
+++ b/test/fixtures/p2pk.json
@@ -74,7 +74,7 @@
},
{
"description": "Non-canonical signature",
- "exception": "Expected property \"signature\" of type \\?isCanonicalScriptSignature, got Buffer",
+ "exception": "ValiError: Expected signature to be of type isCanonicalScriptSignature",
"arguments": {
"pubkey": "030000000000000000000000000000000000000000000000000000000000000001",
"signature": "3044"
diff --git a/test/fixtures/p2pkh.json b/test/fixtures/p2pkh.json
index 4efbb5990..d372a9e44 100644
--- a/test/fixtures/p2pkh.json
+++ b/test/fixtures/p2pkh.json
@@ -124,7 +124,7 @@
},
{
"description": "Non-minimally encoded (non BIP62 compliant)",
- "exception": "Expected property \"output\" of type Buffer\\(Length: 25\\), got Buffer\\(Length: 26\\)",
+ "exception": "ValiError: Invalid length: Expected 25 but received 26",
"arguments": {
"outputHex": "76a94c14aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac"
}
diff --git a/test/fixtures/p2sh.json b/test/fixtures/p2sh.json
index 8a1f9f68e..0ded4ab08 100644
--- a/test/fixtures/p2sh.json
+++ b/test/fixtures/p2sh.json
@@ -204,7 +204,7 @@
},
{
"description": "Non-minimally encoded (non BIP62 compliant)",
- "exception": "Expected property \"output\" of type Buffer\\(Length: 23\\), got Buffer\\(Length: 24\\)",
+ "exception": "ValiError: Invalid length: Expected 23 but received 24",
"arguments": {
"outputHex": "a94c14c286a1af0947f58d1ad787385b1c2c4a976f9e7187"
}
@@ -289,11 +289,25 @@
}
},
{
- "exception": "Input is invalid",
+ "exception": "Redeem.output too short",
"arguments": {
"input": "OP_0 OP_0"
}
},
+ {
+ "exception": "Redeem.output unspendable if larger than 520 bytes",
+ "arguments": {
+ "redeem": {
+ "output": "OP_16 03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0 0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600 0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8 0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8 02da72e8b46901a65d4374fe6315538d8f368557dda3a1dcf9ea903f3afe7314c8 0318c82dd0b53fd3a932d16e0ba9e278fcc937c582d5781be626ff16e201f72286 0297ccef1ef99f9d73dec9ad37476ddb232f1238aff877af19e72ba04493361009 02e502cfd5c3f972fe9a3e2a18827820638f96b6f347e54d63deb839011fd5765d 03e687710f0e3ebe81c1037074da939d409c0025f17eb86adb9427d28f0f7ae0e9 02c04d3a5274952acdbc76987f3184b346a483d43be40874624b29e3692c1df5af 02ed06e0f418b5b43a7ec01d1d7d27290fa15f75771cb69b642a51471c29c84acd 036d46073cbb9ffee90473f3da429abc8de7f8751199da44485682a989a4bebb24 02f5d1ff7c9029a80a4e36b9a5497027ef7f3e73384a4a94fbfe7c4e9164eec8bc 02e41deffd1b7cce11cde209a781adcffdabd1b91c0ba0375857a2bfd9302419f3 02d76625f7956a7fc505ab02556c23ee72d832f1bac391bcd2d3abce5710a13d06 0399eb0a5487515802dc14544cf10b3666623762fbed2ec38a3975716e2c29c232 OP_16 OP_CHECKMULTISIG"
+ }
+ }
+ },
+ {
+ "exception": "Input is invalid",
+ "arguments": {
+ "input": "OP_0 OP_3"
+ }
+ },
{
"exception": "Redeem.input mismatch",
"arguments": {
diff --git a/test/fixtures/p2tr.json b/test/fixtures/p2tr.json
new file mode 100644
index 000000000..4ba31cc57
--- /dev/null
+++ b/test/fixtures/p2tr.json
@@ -0,0 +1,1198 @@
+{
+ "valid": [
+ {
+ "description": "output and pubkey from address",
+ "arguments": {
+ "address": "bc1p4dss6gkgq8003g0qyd5drwfqrztsadf2w2v3juz73gdz7cx82r6sj7lcqx"
+ },
+ "options": {},
+ "expected": {
+ "name": "p2tr",
+ "output": "OP_1 ab610d22c801def8a1e02368d1b92018970eb52a729919705e8a1a2f60c750f5",
+ "pubkey": "ab610d22c801def8a1e02368d1b92018970eb52a729919705e8a1a2f60c750f5",
+ "signature": null,
+ "input": null,
+ "witness": null
+ }
+ },
+ {
+ "description": "address and pubkey from output",
+ "arguments": {
+ "output": "OP_1 ab610d22c801def8a1e02368d1b92018970eb52a729919705e8a1a2f60c750f5"
+ },
+ "expected": {
+ "name": "p2tr",
+ "address": "bc1p4dss6gkgq8003g0qyd5drwfqrztsadf2w2v3juz73gdz7cx82r6sj7lcqx",
+ "pubkey": "ab610d22c801def8a1e02368d1b92018970eb52a729919705e8a1a2f60c750f5",
+ "signature": null,
+ "input": null,
+ "witness": null
+ }
+ },
+ {
+ "description": "address and output from pubkey",
+ "arguments": {
+ "pubkey": "ab610d22c801def8a1e02368d1b92018970eb52a729919705e8a1a2f60c750f5"
+ },
+ "expected": {
+ "name": "p2tr",
+ "address": "bc1p4dss6gkgq8003g0qyd5drwfqrztsadf2w2v3juz73gdz7cx82r6sj7lcqx",
+ "output": "OP_1 ab610d22c801def8a1e02368d1b92018970eb52a729919705e8a1a2f60c750f5",
+ "signature": null,
+ "input": null,
+ "witness": null
+ }
+ },
+ {
+ "description": "address, output and witness from pubkey and signature",
+ "arguments": {
+ "pubkey": "ab610d22c801def8a1e02368d1b92018970eb52a729919705e8a1a2f60c750f5",
+ "signature": "a251221c339a7129dd0b769698aca40d8d9da9570ab796a1820b91ab7dbf5acbea21c88ba8f1e9308a21729baf080734beaf97023882d972f75e380d480fd704"
+ },
+ "expected": {
+ "name": "p2tr",
+ "address": "bc1p4dss6gkgq8003g0qyd5drwfqrztsadf2w2v3juz73gdz7cx82r6sj7lcqx",
+ "output": "OP_1 ab610d22c801def8a1e02368d1b92018970eb52a729919705e8a1a2f60c750f5",
+ "input": null,
+ "witness": [
+ "a251221c339a7129dd0b769698aca40d8d9da9570ab796a1820b91ab7dbf5acbea21c88ba8f1e9308a21729baf080734beaf97023882d972f75e380d480fd704"
+ ]
+ }
+ },
+ {
+ "description": "address, output and signature from pubkey and witness",
+ "arguments": {
+ "pubkey": "ab610d22c801def8a1e02368d1b92018970eb52a729919705e8a1a2f60c750f5",
+ "witness": [
+ "300602010002010001"
+ ]
+ },
+ "expected": {
+ "name": "p2tr",
+ "address": "bc1p4dss6gkgq8003g0qyd5drwfqrztsadf2w2v3juz73gdz7cx82r6sj7lcqx",
+ "output": "OP_1 ab610d22c801def8a1e02368d1b92018970eb52a729919705e8a1a2f60c750f5",
+ "input": null,
+ "signature": "300602010002010001",
+ "witness": [
+ "300602010002010001"
+ ]
+ }
+ },
+ {
+ "description": "address, pubkey and output from internalPubkey",
+ "arguments": {
+ "internalPubkey": "9fa5ffb68821cf559001caa0577eeea4978b29416def328a707b15e91701a2f7"
+ },
+ "expected": {
+ "name": "p2tr",
+ "address": "bc1prs7pxymu7jhsptzjlwlqnk8jyg5qmq4sdlc3rwcy7pd3ydz92xjq5ap2sg",
+ "pubkey": "1c3c13137cf4af00ac52fbbe09d8f222280d82b06ff111bb04f05b12344551a4",
+ "output": "OP_1 1c3c13137cf4af00ac52fbbe09d8f222280d82b06ff111bb04f05b12344551a4",
+ "signature": null,
+ "input": null,
+ "witness": null
+ }
+ },
+ {
+ "description": "address, pubkey, internalPubkey, redeeem and output from witness",
+ "arguments": {
+ "witness": [
+ "9675a9982c6398ea9d441cb7a943bcd6ff033cc3a2e01a0178a7d3be4575be863871c6bf3eef5ecd34721c784259385ca9101c3a313e010ac942c99de05aaaa602",
+ "5799cf4b193b730fb99580b186f7477c2cca4d28957326f6f1a5d14116438530e7ec0ce1cd465ad96968ae8a6a09d4d37a060a115919f56fcfebe7b2277cc2df5cc08fb6cda9105ee2512b2e22635aba",
+ "7520c7b5db9562078049719228db2ac80cb9643ec96c8055aa3b29c2c03d4d99edb0ac",
+ "c1a7957acbaaf7b444c53d9e0c9436e8a8a3247fd515095d66ddf6201918b40a3668f9a4ccdffcf778da624dca2dda0b08e763ec52fd4ad403ec7563a3504d0cc168b9a77a410029e01dac89567c9b2e6cd726e840351df3f2f58fefe976200a19244150d04153909f660184d656ee95fa7bf8e1d4ec83da1fca34f64bc279b76d257ec623e08baba2cfa4ea9e99646e88f1eb1668c00c0f15b7443c8ab83481611cc3ae85eb89a7bfc40067eb1d2e6354a32426d0ce710e88bc4cc0718b99c325509c9d02a6a980d675a8969be10ee9bef82cafee2fc913475667ccda37b1bc7f13f64e56c449c532658ba8481631c02ead979754c809584a875951619cec8fb040c33f06468ae0266cd8693d6a64cea5912be32d8de95a6da6300b0c50fdcd6001ea41126e7b7e5280d455054a816560028f5ca53c9a50ee52f10e15c5337315bad1f5277acb109a1418649dc6ead2fe14699742fee7182f2f15e54279c7d932ed2799d01d73c97e68bbc94d6f7f56ee0a80efd7c76e3169e10d1a1ba3b5f1eb02369dc43af687461c7a2a3344d13eb5485dca29a67f16b4cb988923060fd3b65d0f0352bb634bcc44f2fe668836dcd0f604150049835135dc4b4fbf90fb334b3938a1f137eb32f047c65b85e6c1173b890b6d0162b48b186d1f1af8521945924ac8ac8efec321bf34f1d4b3d4a304a10313052c652d53f6ecb8a55586614e8950cde9ab6fe8e22802e93b3b9139112250b80ebc589aba231af535bb20f7eeec2e412f698c17f3fdc0a2e20924a5e38b21a628a9e3b2a61e35958e60c7f5087c"
+ ]
+ },
+ "expected": {
+ "name": "p2tr",
+ "internalPubkey": "a7957acbaaf7b444c53d9e0c9436e8a8a3247fd515095d66ddf6201918b40a36",
+ "pubkey": "1ebe8b90363bd097aa9f352c8b21914e1886bc09fe9e70c09f33ef2d2abdf4bc",
+ "hash": "c5c62d7fc595ba5fbe61602eb1a29e2e4763408fe1e2b161beb7cb3c71ebcad9",
+ "address": "bc1pr6lghypk80gf025lx5kgkgv3fcvgd0qfl608psylx0hj624a7j7qay80rv",
+ "output": "OP_1 1ebe8b90363bd097aa9f352c8b21914e1886bc09fe9e70c09f33ef2d2abdf4bc",
+ "signature": null,
+ "input": null,
+ "redeem" : {
+ "output": "OP_DROP c7b5db9562078049719228db2ac80cb9643ec96c8055aa3b29c2c03d4d99edb0 OP_CHECKSIG",
+ "redeemVersion": 192,
+ "witness": [
+ "9675a9982c6398ea9d441cb7a943bcd6ff033cc3a2e01a0178a7d3be4575be863871c6bf3eef5ecd34721c784259385ca9101c3a313e010ac942c99de05aaaa602",
+ "5799cf4b193b730fb99580b186f7477c2cca4d28957326f6f1a5d14116438530e7ec0ce1cd465ad96968ae8a6a09d4d37a060a115919f56fcfebe7b2277cc2df5cc08fb6cda9105ee2512b2e22635aba"
+ ]
+ },
+ "witness": [
+ "9675a9982c6398ea9d441cb7a943bcd6ff033cc3a2e01a0178a7d3be4575be863871c6bf3eef5ecd34721c784259385ca9101c3a313e010ac942c99de05aaaa602",
+ "5799cf4b193b730fb99580b186f7477c2cca4d28957326f6f1a5d14116438530e7ec0ce1cd465ad96968ae8a6a09d4d37a060a115919f56fcfebe7b2277cc2df5cc08fb6cda9105ee2512b2e22635aba",
+ "7520c7b5db9562078049719228db2ac80cb9643ec96c8055aa3b29c2c03d4d99edb0ac",
+ "c1a7957acbaaf7b444c53d9e0c9436e8a8a3247fd515095d66ddf6201918b40a3668f9a4ccdffcf778da624dca2dda0b08e763ec52fd4ad403ec7563a3504d0cc168b9a77a410029e01dac89567c9b2e6cd726e840351df3f2f58fefe976200a19244150d04153909f660184d656ee95fa7bf8e1d4ec83da1fca34f64bc279b76d257ec623e08baba2cfa4ea9e99646e88f1eb1668c00c0f15b7443c8ab83481611cc3ae85eb89a7bfc40067eb1d2e6354a32426d0ce710e88bc4cc0718b99c325509c9d02a6a980d675a8969be10ee9bef82cafee2fc913475667ccda37b1bc7f13f64e56c449c532658ba8481631c02ead979754c809584a875951619cec8fb040c33f06468ae0266cd8693d6a64cea5912be32d8de95a6da6300b0c50fdcd6001ea41126e7b7e5280d455054a816560028f5ca53c9a50ee52f10e15c5337315bad1f5277acb109a1418649dc6ead2fe14699742fee7182f2f15e54279c7d932ed2799d01d73c97e68bbc94d6f7f56ee0a80efd7c76e3169e10d1a1ba3b5f1eb02369dc43af687461c7a2a3344d13eb5485dca29a67f16b4cb988923060fd3b65d0f0352bb634bcc44f2fe668836dcd0f604150049835135dc4b4fbf90fb334b3938a1f137eb32f047c65b85e6c1173b890b6d0162b48b186d1f1af8521945924ac8ac8efec321bf34f1d4b3d4a304a10313052c652d53f6ecb8a55586614e8950cde9ab6fe8e22802e93b3b9139112250b80ebc589aba231af535bb20f7eeec2e412f698c17f3fdc0a2e20924a5e38b21a628a9e3b2a61e35958e60c7f5087c"
+ ]
+ }
+ },
+ {
+ "description": "address, pubkey, internalPubkey and output from witness with annex",
+ "arguments": {
+ "witness": [
+ "9675a9982c6398ea9d441cb7a943bcd6ff033cc3a2e01a0178a7d3be4575be863871c6bf3eef5ecd34721c784259385ca9101c3a313e010ac942c99de05aaaa602",
+ "5799cf4b193b730fb99580b186f7477c2cca4d28957326f6f1a5d14116438530e7ec0ce1cd465ad96968ae8a6a09d4d37a060a115919f56fcfebe7b2277cc2df5cc08fb6cda9105ee2512b2e22635aba",
+ "7520c7b5db9562078049719228db2ac80cb9643ec96c8055aa3b29c2c03d4d99edb0ac",
+ "c1a7957acbaaf7b444c53d9e0c9436e8a8a3247fd515095d66ddf6201918b40a3668f9a4ccdffcf778da624dca2dda0b08e763ec52fd4ad403ec7563a3504d0cc168b9a77a410029e01dac89567c9b2e6cd726e840351df3f2f58fefe976200a19244150d04153909f660184d656ee95fa7bf8e1d4ec83da1fca34f64bc279b76d257ec623e08baba2cfa4ea9e99646e88f1eb1668c00c0f15b7443c8ab83481611cc3ae85eb89a7bfc40067eb1d2e6354a32426d0ce710e88bc4cc0718b99c325509c9d02a6a980d675a8969be10ee9bef82cafee2fc913475667ccda37b1bc7f13f64e56c449c532658ba8481631c02ead979754c809584a875951619cec8fb040c33f06468ae0266cd8693d6a64cea5912be32d8de95a6da6300b0c50fdcd6001ea41126e7b7e5280d455054a816560028f5ca53c9a50ee52f10e15c5337315bad1f5277acb109a1418649dc6ead2fe14699742fee7182f2f15e54279c7d932ed2799d01d73c97e68bbc94d6f7f56ee0a80efd7c76e3169e10d1a1ba3b5f1eb02369dc43af687461c7a2a3344d13eb5485dca29a67f16b4cb988923060fd3b65d0f0352bb634bcc44f2fe668836dcd0f604150049835135dc4b4fbf90fb334b3938a1f137eb32f047c65b85e6c1173b890b6d0162b48b186d1f1af8521945924ac8ac8efec321bf34f1d4b3d4a304a10313052c652d53f6ecb8a55586614e8950cde9ab6fe8e22802e93b3b9139112250b80ebc589aba231af535bb20f7eeec2e412f698c17f3fdc0a2e20924a5e38b21a628a9e3b2a61e35958e60c7f5087c",
+ "5000000000000000001111111111111111"
+ ]
+ },
+ "expected": {
+ "name": "p2tr",
+ "internalPubkey": "a7957acbaaf7b444c53d9e0c9436e8a8a3247fd515095d66ddf6201918b40a36",
+ "pubkey": "1ebe8b90363bd097aa9f352c8b21914e1886bc09fe9e70c09f33ef2d2abdf4bc",
+ "hash": "c5c62d7fc595ba5fbe61602eb1a29e2e4763408fe1e2b161beb7cb3c71ebcad9",
+ "address": "bc1pr6lghypk80gf025lx5kgkgv3fcvgd0qfl608psylx0hj624a7j7qay80rv",
+ "output": "OP_1 1ebe8b90363bd097aa9f352c8b21914e1886bc09fe9e70c09f33ef2d2abdf4bc",
+ "signature": null,
+ "input": null,
+ "witness": [
+ "9675a9982c6398ea9d441cb7a943bcd6ff033cc3a2e01a0178a7d3be4575be863871c6bf3eef5ecd34721c784259385ca9101c3a313e010ac942c99de05aaaa602",
+ "5799cf4b193b730fb99580b186f7477c2cca4d28957326f6f1a5d14116438530e7ec0ce1cd465ad96968ae8a6a09d4d37a060a115919f56fcfebe7b2277cc2df5cc08fb6cda9105ee2512b2e22635aba",
+ "7520c7b5db9562078049719228db2ac80cb9643ec96c8055aa3b29c2c03d4d99edb0ac",
+ "c1a7957acbaaf7b444c53d9e0c9436e8a8a3247fd515095d66ddf6201918b40a3668f9a4ccdffcf778da624dca2dda0b08e763ec52fd4ad403ec7563a3504d0cc168b9a77a410029e01dac89567c9b2e6cd726e840351df3f2f58fefe976200a19244150d04153909f660184d656ee95fa7bf8e1d4ec83da1fca34f64bc279b76d257ec623e08baba2cfa4ea9e99646e88f1eb1668c00c0f15b7443c8ab83481611cc3ae85eb89a7bfc40067eb1d2e6354a32426d0ce710e88bc4cc0718b99c325509c9d02a6a980d675a8969be10ee9bef82cafee2fc913475667ccda37b1bc7f13f64e56c449c532658ba8481631c02ead979754c809584a875951619cec8fb040c33f06468ae0266cd8693d6a64cea5912be32d8de95a6da6300b0c50fdcd6001ea41126e7b7e5280d455054a816560028f5ca53c9a50ee52f10e15c5337315bad1f5277acb109a1418649dc6ead2fe14699742fee7182f2f15e54279c7d932ed2799d01d73c97e68bbc94d6f7f56ee0a80efd7c76e3169e10d1a1ba3b5f1eb02369dc43af687461c7a2a3344d13eb5485dca29a67f16b4cb988923060fd3b65d0f0352bb634bcc44f2fe668836dcd0f604150049835135dc4b4fbf90fb334b3938a1f137eb32f047c65b85e6c1173b890b6d0162b48b186d1f1af8521945924ac8ac8efec321bf34f1d4b3d4a304a10313052c652d53f6ecb8a55586614e8950cde9ab6fe8e22802e93b3b9139112250b80ebc589aba231af535bb20f7eeec2e412f698c17f3fdc0a2e20924a5e38b21a628a9e3b2a61e35958e60c7f5087c",
+ "5000000000000000001111111111111111"
+ ]
+ }
+ },
+ {
+ "description": "address, pubkey, output and hash from internalPubkey and a script tree with one leaf",
+ "arguments": {
+ "internalPubkey": "50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0",
+ "scriptTree": {
+ "output": "83d8ee77a0f3a32a5cea96fd1624d623b836c1e5d1ac2dcde46814b619320c18 OP_CHECKSIG"
+ }
+ },
+ "expected": {
+ "name": "p2tr",
+ "address": "bc1pjegs09vkeder9m4sw3ycjf2rnpa8nljdqmuleunk9eshu8cq3xysvhgp2u",
+ "pubkey": "9651079596cb7232eeb07449892543987a79fe4d06f9fcf2762e617e1f008989",
+ "output": "OP_1 9651079596cb7232eeb07449892543987a79fe4d06f9fcf2762e617e1f008989",
+ "hash": "16e3f3b8b9c1e453c56b547785cdd25259d65823a2064f30783acc58ef012633",
+ "signature": null,
+ "input": null,
+ "witness": null
+ }
+ },
+ {
+ "description": "address, pubkey, output and hash from internalPubkey and a script tree with two leafs",
+ "arguments": {
+ "internalPubkey": "2258b1c3160be0864a541854eec9164a572f094f7562628281a8073bb89173a7",
+ "scriptTree": [
+ {
+ "output": "d826a0a53abb6ffc60df25b9c152870578faef4b2eb5a09bdd672bbe32cdd79b OP_CHECKSIG"
+ },
+ {
+ "output": "d826a0a53abb6ffc60df25b9c152870578faef4b2eb5a09bdd672bbe32cdd79b OP_CHECKSIG"
+ }
+ ]
+ },
+ "expected": {
+ "name": "p2tr",
+ "address": "bc1ptj0v8rwcj6s36p4r26ws6htx0fct43n0mxdvdeh9043whlxlq3kq9965ke",
+ "pubkey": "5c9ec38dd896a11d06a3569d0d5d667a70bac66fd99ac6e6e57d62ebfcdf046c",
+ "output": "OP_1 5c9ec38dd896a11d06a3569d0d5d667a70bac66fd99ac6e6e57d62ebfcdf046c",
+ "hash": "ce00198cd4667abae1f94aa5862d089e2967af5aec20715c692db74e3d66bb73",
+ "signature": null,
+ "input": null,
+ "witness": null
+ }
+ },
+ {
+ "description": "address, pubkey, output and hash from internalPubkey and a script tree with three leafs",
+ "arguments": {
+ "internalPubkey": "7631cacec3343052d87ef4d0065f61dde82d7d2db0c1cc02ef61ef3c982ea763",
+ "scriptTree": [
+ {
+ "output": "50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0 OP_CHECKSIG"
+ },
+ [
+ {
+ "output": "50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0 OP_CHECKSIG"
+ },
+ {
+ "output": "9b4d495b74887815a1ff623c055c6eac6b6b2e07d2a016d6526ebac71dd99744 OP_CHECKSIG"
+ }
+ ]
+ ]
+ },
+ "expected": {
+ "name": "p2tr",
+ "address": "bc1pkq0t8nkmqswn3qjg9uy6ux2hsyyz4as25v8unfjc9s8q2e4c00sqku9lxh",
+ "pubkey": "b01eb3cedb041d3882482f09ae195781082af60aa30fc9a6582c0e0566b87be0",
+ "output": "OP_1 b01eb3cedb041d3882482f09ae195781082af60aa30fc9a6582c0e0566b87be0",
+ "hash": "7ae0cc2057b1a7bf0e09c787e1d7b6b2355ac112a7b80380a5c1e942155b0c0f",
+ "signature": null,
+ "input": null,
+ "witness": null
+ }
+ },
+ {
+ "description": "address, pubkey, output and hash from internalPubkey and a script tree with four leafs",
+ "arguments": {
+ "internalPubkey": "d0c19def28bb1b39451c1a814737615983967780d223b79969ba692182c6006b",
+ "scriptTree": [
+ [
+ {
+ "output": "9b4d495b74887815a1ff623c055c6eac6b6b2e07d2a016d6526ebac71dd99744 OP_CHECKSIG"
+ },
+ {
+ "output": "50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0 OP_CHECKSIG"
+ }
+ ],
+ [
+ {
+ "output": "50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0 OP_CHECKSIG"
+ },
+ {
+ "output": "9b4d495b74887815a1ff623c055c6eac6b6b2e07d2a016d6526ebac71dd99744 OP_CHECKSIG"
+ }
+ ]
+ ]
+ },
+ "expected": {
+ "name": "p2tr",
+ "address": "bc1pstdzevc40j059s0473rghhv9e05l9f5xv7l6dtlavvq22rzfna3syjvjut",
+ "pubkey": "82da2cb3157c9f42c1f5f4468bdd85cbe9f2a68667bfa6affd6300a50c499f63",
+ "output": "OP_1 82da2cb3157c9f42c1f5f4468bdd85cbe9f2a68667bfa6affd6300a50c499f63",
+ "hash": "d673e784eac9b70289130a0bd359023a0fbdde51dc069b9efb4157c2cdce3ea5",
+ "signature": null,
+ "input": null,
+ "witness": null
+ }
+ },
+ {
+ "description": "address, pubkey, output and hash from internalPubkey and a script tree with seven leafs",
+ "arguments": {
+ "internalPubkey": "f95886b02a84928c5c15bdca32784993105f73de27fa6ad8c1a60389b999267c",
+ "scriptTree": [
+ [
+ {
+ "output": "50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0 OP_CHECKSIG"
+ },
+ [
+ {
+ "output": "50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0 OP_CHECKSIG"
+ },
+ {
+ "output": "2258b1c3160be0864a541854eec9164a572f094f7562628281a8073bb89173a7 OP_CHECKSIG"
+ }
+ ]
+ ],
+ [
+ {
+ "output": "50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0 OP_CHECKSIG"
+ },
+ [
+ {
+ "output": "50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0 OP_CHECKSIG"
+ },
+ [
+ {
+ "output": "50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0 OP_CHECKSIG"
+ },
+ {
+ "output": "03a669ea926f381582ec4a000b9472ba8a17347f5fb159eddd4a07036a6718eb OP_CHECKSIG"
+ }
+ ]
+ ]
+ ]
+ ]
+ },
+ "expected": {
+ "name": "p2tr",
+ "address": "bc1pfas4r5s5208puwzj20hvwg2dw2kanc06yxczzdd66729z63pk43q7zwlu6",
+ "pubkey": "4f6151d21453ce1e385253eec7214d72add9e1fa21b02135bad794516a21b562",
+ "output": "OP_1 4f6151d21453ce1e385253eec7214d72add9e1fa21b02135bad794516a21b562",
+ "hash": "16fb2e99bdf86f67ee6980d0418658f15df7e19476053b58f45a89df2e219b1b",
+ "signature": null,
+ "input": null,
+ "witness": null
+ }
+ },
+ {
+ "description": "address, pubkey, and output from internalPubkey redeem, and hash (one leaf, no tree)",
+ "arguments": {
+ "internalPubkey": "aba457d16a8d59151c387f24d1eb887efbe24644c1ee64b261282e7baebdb247",
+ "redeem": {
+ "output": "50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac4 OP_CHECKSIG"
+ },
+ "hash": "b424dea09f840b932a00373cdcdbd25650b8c3acfe54a9f4a641a286721b8d26"
+ },
+ "expected": {
+ "name": "p2tr",
+ "address": "bc1pnxyp0ahcg53jzgrzj57hnlgdtqtzn7qqhmgjgczk8hzhcltq974qazepzf",
+ "pubkey": "998817f6f84523212062953d79fd0d581629f800bed12460563dc57c7d602faa",
+ "output": "OP_1 998817f6f84523212062953d79fd0d581629f800bed12460563dc57c7d602faa",
+ "signature": null,
+ "input": null,
+ "witness": [
+ "2050929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac4ac",
+ "c0aba457d16a8d59151c387f24d1eb887efbe24644c1ee64b261282e7baebdb247"
+ ]
+ }
+ },
+ {
+ "description": "address, pubkey, output and hash from internalPubkey and a script tree with seven leafs (2)",
+ "arguments": {
+ "internalPubkey": "aba457d16a8d59151c387f24d1eb887efbe24644c1ee64b261282e7baebdb247",
+ "redeem": {
+ "output": "50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac4 OP_CHECKSIG"
+ },
+ "scriptTree": [
+ {
+ "output": "00a9da96087a72258f83b338ef7f0ea8cbbe05da5f18f091eb397d1ecbf7c3d3 OP_CHECKSIG"
+ },
+ [
+ [
+ {
+ "output": "00a9da96087a72258f83b338ef7f0ea8cbbe05da5f18f091eb397d1ecbf7c3d4 OP_CHECKSIG"
+ },
+ [
+ {
+ "output": "50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac1 OP_CHECKSIG"
+ },
+ {
+ "output": "50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac2 OP_CHECKSIG"
+ }
+ ]
+ ],
+ [
+ [
+ {
+ "output": "50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac3 OP_CHECKSIG"
+ },
+ {
+ "output": "50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac4 OP_CHECKSIG"
+ }
+ ],
+ {
+ "output": "00a9da96087a72258f83b338ef7f0ea8cbbe05da5f18f091eb397d1ecbf7c3d5 OP_CHECKSIG"
+ }
+ ]
+ ]
+ ]
+ },
+ "expected": {
+ "name": "p2tr",
+ "address": "bc1pd2llmtym6c5hyecf5zqsyjz9q0jlxaaksw9j0atx8lc8a0e0vrmsw9ewly",
+ "pubkey": "6abffdac9bd629726709a08102484503e5f377b6838b27f5663ff07ebf2f60f7",
+ "output": "OP_1 6abffdac9bd629726709a08102484503e5f377b6838b27f5663ff07ebf2f60f7",
+ "hash": "88b7e3b495a84aa2bc12780b1773f130ce5eb747b0c28dc4840b7c9280f7326d",
+ "signature": null,
+ "input": null,
+ "witness": [
+ "2050929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac4ac",
+ "c0aba457d16a8d59151c387f24d1eb887efbe24644c1ee64b261282e7baebdb247dac795766bbda1eaeaa45e5bfa0a950fdd5f4c4aada5b1f3082edc9689b9fd0a315fb34a7a93dcaed5e26cf7468be5bd377dda7a4d29128f7dd98db6da9bf04325fff3aa86365bac7534dcb6495867109941ec444dd35294e0706e29e051066d73e0d427bd3249bb921fa78c04fb76511f583ff48c97210d17c2d9dcfbb95023"
+ ]
+ }
+ },
+ {
+ "description": "BIP341 Test case 1",
+ "arguments": {
+ "internalPubkey": "d6889cb081036e0faefa3a35157ad71086b123b2b144b649798b494c300a961d"
+ },
+ "options": {},
+ "expected": {
+ "name": "p2tr",
+ "output": "OP_1 53a1f6e454df1aa2776a2814a721372d6258050de330b3c6d10ee8f4e0dda343",
+ "pubkey": "53a1f6e454df1aa2776a2814a721372d6258050de330b3c6d10ee8f4e0dda343",
+ "address": "bc1p2wsldez5mud2yam29q22wgfh9439spgduvct83k3pm50fcxa5dps59h4z5",
+ "signature": null,
+ "input": null,
+ "witness": null
+ }
+ },
+ {
+ "description": "BIP341 Test case 2",
+ "arguments": {
+ "internalPubkey": "187791b6f712a8ea41c8ecdd0ee77fab3e85263b37e1ec18a3651926b3a6cf27",
+ "redeem": {
+ "output": "d85a959b0290bf19bb89ed43c916be835475d013da4b362117393e25a48229b8 OP_CHECKSIG",
+ "redeemVersion": 192
+ },
+ "scriptTree": {
+ "output": "d85a959b0290bf19bb89ed43c916be835475d013da4b362117393e25a48229b8 OP_CHECKSIG",
+ "version": 192
+ }
+ },
+ "options": {},
+ "expected": {
+ "name": "p2tr",
+ "output": "OP_1 147c9c57132f6e7ecddba9800bb0c4449251c92a1e60371ee77557b6620f3ea3",
+ "pubkey": "147c9c57132f6e7ecddba9800bb0c4449251c92a1e60371ee77557b6620f3ea3",
+ "address": "bc1pz37fc4cn9ah8anwm4xqqhvxygjf9rjf2resrw8h8w4tmvcs0863sa2e586",
+ "hash": "5b75adecf53548f3ec6ad7d78383bf84cc57b55a3127c72b9a2481752dd88b21",
+ "witness": [
+ "20d85a959b0290bf19bb89ed43c916be835475d013da4b362117393e25a48229b8ac",
+ "c1187791b6f712a8ea41c8ecdd0ee77fab3e85263b37e1ec18a3651926b3a6cf27"
+ ],
+ "redeem": {
+ "output": "d85a959b0290bf19bb89ed43c916be835475d013da4b362117393e25a48229b8 OP_CHECKSIG",
+ "redeemVersion": 192
+ },
+ "signature": null,
+ "input": null
+ }
+ },
+ {
+ "description": "BIP341 Test case 3",
+ "arguments": {
+ "internalPubkey": "93478e9488f956df2396be2ce6c5cced75f900dfa18e7dabd2428aae78451820",
+ "redeem": {
+ "output": "b617298552a72ade070667e86ca63b8f5789a9fe8731ef91202a91c9f3459007 OP_CHECKSIG",
+ "redeemVersion": 192
+ },
+ "scriptTree": {
+ "output": "b617298552a72ade070667e86ca63b8f5789a9fe8731ef91202a91c9f3459007 OP_CHECKSIG",
+ "version": 192
+ }
+ },
+ "options": {},
+ "expected": {
+ "name": "p2tr",
+ "output": "OP_1 e4d810fd50586274face62b8a807eb9719cef49c04177cc6b76a9a4251d5450e",
+ "pubkey": "e4d810fd50586274face62b8a807eb9719cef49c04177cc6b76a9a4251d5450e",
+ "address": "bc1punvppl2stp38f7kwv2u2spltjuvuaayuqsthe34hd2dyy5w4g58qqfuag5",
+ "hash": "c525714a7f49c28aedbbba78c005931a81c234b2f6c99a73e4d06082adc8bf2b",
+ "witness": [
+ "20b617298552a72ade070667e86ca63b8f5789a9fe8731ef91202a91c9f3459007ac",
+ "c093478e9488f956df2396be2ce6c5cced75f900dfa18e7dabd2428aae78451820"
+ ],
+ "signature": null,
+ "input": null
+ }
+ },
+ {
+ "description": "BIP341 Test case 4 - spend leaf 0",
+ "arguments": {
+ "internalPubkey": "ee4fe085983462a184015d1f782d6a5f8b9c2b60130aff050ce221ecf3786592",
+ "redeem": {
+ "output": "387671353e273264c495656e27e39ba899ea8fee3bb69fb2a680e22093447d48 OP_CHECKSIG",
+ "redeemVersion": 192
+ },
+ "scriptTree": [
+ {
+ "output": "387671353e273264c495656e27e39ba899ea8fee3bb69fb2a680e22093447d48 OP_CHECKSIG",
+ "version": 192
+ },
+ {
+ "output": "424950333431",
+ "version": 152
+ }
+ ]
+ },
+ "options": {},
+ "expected": {
+ "name": "p2tr",
+ "output": "OP_1 0f63ca2c7639b9bb4be0465cc0aa3ee78a0761ba5f5f7d6ff8eab340f09da561",
+ "pubkey": "0f63ca2c7639b9bb4be0465cc0aa3ee78a0761ba5f5f7d6ff8eab340f09da561",
+ "address": "bc1ppa3u5trk8xumkjlqgewvp237u79qwcd6ta0h6mlca2e5puya54ssw9zq0y",
+ "hash": "f3004d6c183e038105d436db1424f321613366cbb7b05939bf05d763a9ebb962",
+ "witness": [
+ "20387671353e273264c495656e27e39ba899ea8fee3bb69fb2a680e22093447d48ac",
+ "c0ee4fe085983462a184015d1f782d6a5f8b9c2b60130aff050ce221ecf37865927b2c2af8aa3e8b7bfe2f62a155f91427489c5c3b32be47e0b3fac755fc780e0e"
+ ],
+ "signature": null,
+ "input": null
+ }
+ },
+ {
+ "description": "BIP341 Test case 4 - spend leaf 1",
+ "arguments": {
+ "internalPubkey": "ee4fe085983462a184015d1f782d6a5f8b9c2b60130aff050ce221ecf3786592",
+ "redeem": {
+ "output": "424950333431",
+ "redeemVersion": 152
+ },
+ "scriptTree": [
+ {
+ "output": "387671353e273264c495656e27e39ba899ea8fee3bb69fb2a680e22093447d48 OP_CHECKSIG",
+ "version": 192
+ },
+ {
+ "output": "424950333431",
+ "version": 152
+ }
+ ]
+ },
+ "options": {},
+ "expected": {
+ "name": "p2tr",
+ "output": "OP_1 0f63ca2c7639b9bb4be0465cc0aa3ee78a0761ba5f5f7d6ff8eab340f09da561",
+ "pubkey": "0f63ca2c7639b9bb4be0465cc0aa3ee78a0761ba5f5f7d6ff8eab340f09da561",
+ "address": "bc1ppa3u5trk8xumkjlqgewvp237u79qwcd6ta0h6mlca2e5puya54ssw9zq0y",
+ "hash": "f3004d6c183e038105d436db1424f321613366cbb7b05939bf05d763a9ebb962",
+ "witness": [
+ "06424950333431",
+ "98ee4fe085983462a184015d1f782d6a5f8b9c2b60130aff050ce221ecf37865928ad69ec7cf41c2a4001fd1f738bf1e505ce2277acdcaa63fe4765192497f47a7"
+ ],
+ "signature": null,
+ "input": null
+ }
+ },
+ {
+ "description": "BIP341 Test case 5 - spend leaf 0",
+ "arguments": {
+ "internalPubkey": "f9f400803e683727b14f463836e1e78e1c64417638aa066919291a225f0e8dd8",
+ "redeem": {
+ "output": "44b178d64c32c4a05cc4f4d1407268f764c940d20ce97abfd44db5c3592b72fd OP_CHECKSIG",
+ "redeemVersion": 192
+ },
+ "scriptTree": [
+ {
+ "output": "44b178d64c32c4a05cc4f4d1407268f764c940d20ce97abfd44db5c3592b72fd OP_CHECKSIG",
+ "version": 192
+ },
+ {
+ "output": "546170726f6f74",
+ "version": 82
+ }
+ ]
+ },
+ "options": {},
+ "expected": {
+ "name": "p2tr",
+ "output": "OP_1 053690babeabbb7850c32eead0acf8df990ced79f7a31e358fabf2658b4bc587",
+ "pubkey": "053690babeabbb7850c32eead0acf8df990ced79f7a31e358fabf2658b4bc587",
+ "address": "bc1pq5mfpw474wahs5xr9m4dpt8cm7vsemte7733udv040extz6tckrs29g04c",
+ "hash": "d9c2c32808b41c0301d876d49c0af72e1d98e84b99ca9b4bb67fea1a7424b755",
+ "witness": [
+ "2044b178d64c32c4a05cc4f4d1407268f764c940d20ce97abfd44db5c3592b72fdac",
+ "c1f9f400803e683727b14f463836e1e78e1c64417638aa066919291a225f0e8dd8e44d5f8fa5892c8b6d4d09a08d36edd0b08636e30311302e2448ad8172fb3433"
+ ],
+ "signature": null,
+ "input": null
+ }
+ },
+ {
+ "description": "BIP341 Test case 5 - spend leaf 1",
+ "arguments": {
+ "internalPubkey": "f9f400803e683727b14f463836e1e78e1c64417638aa066919291a225f0e8dd8",
+ "redeem": {
+ "output": "546170726f6f74",
+ "redeemVersion": 82
+ },
+ "scriptTree": [
+ {
+ "output": "44b178d64c32c4a05cc4f4d1407268f764c940d20ce97abfd44db5c3592b72fd OP_CHECKSIG",
+ "version": 192
+ },
+ {
+ "output": "546170726f6f74",
+ "version": 82
+ }
+ ]
+ },
+ "options": {},
+ "expected": {
+ "name": "p2tr",
+ "output": "OP_1 053690babeabbb7850c32eead0acf8df990ced79f7a31e358fabf2658b4bc587",
+ "pubkey": "053690babeabbb7850c32eead0acf8df990ced79f7a31e358fabf2658b4bc587",
+ "address": "bc1pq5mfpw474wahs5xr9m4dpt8cm7vsemte7733udv040extz6tckrs29g04c",
+ "hash": "d9c2c32808b41c0301d876d49c0af72e1d98e84b99ca9b4bb67fea1a7424b755",
+ "witness": [
+ "07546170726f6f74",
+ "53f9f400803e683727b14f463836e1e78e1c64417638aa066919291a225f0e8dd864512fecdb5afa04f98839b50e6f0cb7b1e539bf6f205f67934083cdcc3c8d89"
+ ],
+ "signature": null,
+ "input": null
+ }
+ },
+ {
+ "description": "BIP341 Test case 6 - spend leaf 0",
+ "arguments": {
+ "internalPubkey": "e0dfe2300b0dd746a3f8674dfd4525623639042569d829c7f0eed9602d263e6f",
+ "redeem": {
+ "output": "72ea6adcf1d371dea8fba1035a09f3d24ed5a059799bae114084130ee5898e69 OP_CHECKSIG",
+ "redeemVersion": 192
+ },
+ "scriptTree": [
+ {
+ "output": "72ea6adcf1d371dea8fba1035a09f3d24ed5a059799bae114084130ee5898e69 OP_CHECKSIG",
+ "version": 192
+ },
+ [
+ {
+ "output": "2352d137f2f3ab38d1eaa976758873377fa5ebb817372c71e2c542313d4abda8 OP_CHECKSIG",
+ "version": 192
+ },
+ {
+ "output": "7337c0dd4253cb86f2c43a2351aadd82cccb12a172cd120452b9bb8324f2186a OP_CHECKSIG",
+ "version": 192
+ }
+ ]
+ ]
+ },
+ "options": {},
+ "expected": {
+ "name": "p2tr",
+ "output": "OP_1 91b64d5324723a985170e4dc5a0f84c041804f2cd12660fa5dec09fc21783605",
+ "pubkey": "91b64d5324723a985170e4dc5a0f84c041804f2cd12660fa5dec09fc21783605",
+ "address": "bc1pjxmy65eywgafs5tsunw95ruycpqcqnev6ynxp7jaasylcgtcxczs6n332e",
+ "hash": "ccbd66c6f7e8fdab47b3a486f59d28262be857f30d4773f2d5ea47f7761ce0e2",
+ "witness": [
+ "2072ea6adcf1d371dea8fba1035a09f3d24ed5a059799bae114084130ee5898e69ac",
+ "c0e0dfe2300b0dd746a3f8674dfd4525623639042569d829c7f0eed9602d263e6fffe578e9ea769027e4f5a3de40732f75a88a6353a09d767ddeb66accef85e553"
+ ],
+ "signature": null,
+ "input": null
+ }
+ },
+ {
+ "description": "BIP341 Test case 6 - spend leaf 1",
+ "arguments": {
+ "internalPubkey": "e0dfe2300b0dd746a3f8674dfd4525623639042569d829c7f0eed9602d263e6f",
+ "redeem": {
+ "output": "2352d137f2f3ab38d1eaa976758873377fa5ebb817372c71e2c542313d4abda8 OP_CHECKSIG",
+ "redeemVersion": 192
+ },
+ "scriptTree": [
+ {
+ "output": "72ea6adcf1d371dea8fba1035a09f3d24ed5a059799bae114084130ee5898e69 OP_CHECKSIG",
+ "version": 192
+ },
+ [
+ {
+ "output": "2352d137f2f3ab38d1eaa976758873377fa5ebb817372c71e2c542313d4abda8 OP_CHECKSIG",
+ "version": 192
+ },
+ {
+ "output": "7337c0dd4253cb86f2c43a2351aadd82cccb12a172cd120452b9bb8324f2186a OP_CHECKSIG",
+ "version": 192
+ }
+ ]
+ ]
+ },
+ "options": {},
+ "expected": {
+ "name": "p2tr",
+ "output": "OP_1 91b64d5324723a985170e4dc5a0f84c041804f2cd12660fa5dec09fc21783605",
+ "pubkey": "91b64d5324723a985170e4dc5a0f84c041804f2cd12660fa5dec09fc21783605",
+ "address": "bc1pjxmy65eywgafs5tsunw95ruycpqcqnev6ynxp7jaasylcgtcxczs6n332e",
+ "hash": "ccbd66c6f7e8fdab47b3a486f59d28262be857f30d4773f2d5ea47f7761ce0e2",
+ "witness": [
+ "202352d137f2f3ab38d1eaa976758873377fa5ebb817372c71e2c542313d4abda8ac",
+ "c0e0dfe2300b0dd746a3f8674dfd4525623639042569d829c7f0eed9602d263e6f9e31407bffa15fefbf5090b149d53959ecdf3f62b1246780238c24501d5ceaf62645a02e0aac1fe69d69755733a9b7621b694bb5b5cde2bbfc94066ed62b9817"
+ ],
+ "signature": null,
+ "input": null
+ }
+ },
+ {
+ "description": "BIP341 Test case 6 - spend leaf 2",
+ "arguments": {
+ "internalPubkey": "e0dfe2300b0dd746a3f8674dfd4525623639042569d829c7f0eed9602d263e6f",
+ "redeem": {
+ "output": "7337c0dd4253cb86f2c43a2351aadd82cccb12a172cd120452b9bb8324f2186a OP_CHECKSIG",
+ "redeemVersion": 192
+ },
+ "scriptTree": [
+ {
+ "output": "72ea6adcf1d371dea8fba1035a09f3d24ed5a059799bae114084130ee5898e69 OP_CHECKSIG",
+ "version": 192
+ },
+ [
+ {
+ "output": "2352d137f2f3ab38d1eaa976758873377fa5ebb817372c71e2c542313d4abda8 OP_CHECKSIG",
+ "version": 192
+ },
+ {
+ "output": "7337c0dd4253cb86f2c43a2351aadd82cccb12a172cd120452b9bb8324f2186a OP_CHECKSIG",
+ "version": 192
+ }
+ ]
+ ]
+ },
+ "options": {},
+ "expected": {
+ "name": "p2tr",
+ "output": "OP_1 91b64d5324723a985170e4dc5a0f84c041804f2cd12660fa5dec09fc21783605",
+ "pubkey": "91b64d5324723a985170e4dc5a0f84c041804f2cd12660fa5dec09fc21783605",
+ "address": "bc1pjxmy65eywgafs5tsunw95ruycpqcqnev6ynxp7jaasylcgtcxczs6n332e",
+ "hash": "ccbd66c6f7e8fdab47b3a486f59d28262be857f30d4773f2d5ea47f7761ce0e2",
+ "witness": [
+ "207337c0dd4253cb86f2c43a2351aadd82cccb12a172cd120452b9bb8324f2186aac",
+ "c0e0dfe2300b0dd746a3f8674dfd4525623639042569d829c7f0eed9602d263e6fba982a91d4fc552163cb1c0da03676102d5b7a014304c01f0c77b2b8e888de1c2645a02e0aac1fe69d69755733a9b7621b694bb5b5cde2bbfc94066ed62b9817"
+ ],
+ "signature": null,
+ "input": null
+ }
+ },
+ {
+ "description": "BIP341 Test case 7 - spend leaf 0",
+ "arguments": {
+ "internalPubkey": "55adf4e8967fbd2e29f20ac896e60c3b0f1d5b0efa9d34941b5958c7b0a0312d",
+ "redeem": {
+ "output": "71981521ad9fc9036687364118fb6ccd2035b96a423c59c5430e98310a11abe2 OP_CHECKSIG"
+ },
+ "scriptTree": [
+ {
+ "output": "71981521ad9fc9036687364118fb6ccd2035b96a423c59c5430e98310a11abe2 OP_CHECKSIG",
+ "version": 192
+ },
+ [
+ {
+ "output": "d5094d2dbe9b76e2c245a2b89b6006888952e2faa6a149ae318d69e520617748 OP_CHECKSIG",
+ "version": 192
+ },
+ {
+ "output": "c440b462ad48c7a77f94cd4532d8f2119dcebbd7c9764557e62726419b08ad4c OP_CHECKSIG",
+ "version": 192
+ }
+ ]
+ ]
+ },
+ "options": {},
+ "expected": {
+ "name": "p2tr",
+ "output": "OP_1 75169f4001aa68f15bbed28b218df1d0a62cbbcf1188c6665110c293c907b831",
+ "pubkey": "75169f4001aa68f15bbed28b218df1d0a62cbbcf1188c6665110c293c907b831",
+ "address": "bc1pw5tf7sqp4f50zka7629jrr036znzew70zxyvvej3zrpf8jg8hqcssyuewe",
+ "hash": "2f6b2c5397b6d68ca18e09a3f05161668ffe93a988582d55c6f07bd5b3329def",
+ "witness": [
+ "2071981521ad9fc9036687364118fb6ccd2035b96a423c59c5430e98310a11abe2ac",
+ "c155adf4e8967fbd2e29f20ac896e60c3b0f1d5b0efa9d34941b5958c7b0a0312d3cd369a528b326bc9d2133cbd2ac21451acb31681a410434672c8e34fe757e91"
+ ],
+ "signature": null,
+ "input": null
+ }
+ },
+ {
+ "description": "BIP341 Test case 7 - spend leaf 1",
+ "arguments": {
+ "internalPubkey": "55adf4e8967fbd2e29f20ac896e60c3b0f1d5b0efa9d34941b5958c7b0a0312d",
+ "redeem": {
+ "output": "d5094d2dbe9b76e2c245a2b89b6006888952e2faa6a149ae318d69e520617748 OP_CHECKSIG",
+ "redeemVersion": 192
+ },
+ "scriptTree": [
+ {
+ "output": "71981521ad9fc9036687364118fb6ccd2035b96a423c59c5430e98310a11abe2 OP_CHECKSIG",
+ "version": 192
+ },
+ [
+ {
+ "output": "d5094d2dbe9b76e2c245a2b89b6006888952e2faa6a149ae318d69e520617748 OP_CHECKSIG",
+ "version": 192
+ },
+ {
+ "output": "c440b462ad48c7a77f94cd4532d8f2119dcebbd7c9764557e62726419b08ad4c OP_CHECKSIG",
+ "version": 192
+ }
+ ]
+ ]
+ },
+ "options": {},
+ "expected": {
+ "name": "p2tr",
+ "output": "OP_1 75169f4001aa68f15bbed28b218df1d0a62cbbcf1188c6665110c293c907b831",
+ "pubkey": "75169f4001aa68f15bbed28b218df1d0a62cbbcf1188c6665110c293c907b831",
+ "address": "bc1pw5tf7sqp4f50zka7629jrr036znzew70zxyvvej3zrpf8jg8hqcssyuewe",
+ "hash": "2f6b2c5397b6d68ca18e09a3f05161668ffe93a988582d55c6f07bd5b3329def",
+ "witness": [
+ "20d5094d2dbe9b76e2c245a2b89b6006888952e2faa6a149ae318d69e520617748ac",
+ "c155adf4e8967fbd2e29f20ac896e60c3b0f1d5b0efa9d34941b5958c7b0a0312dd7485025fceb78b9ed667db36ed8b8dc7b1f0b307ac167fa516fe4352b9f4ef7f154e8e8e17c31d3462d7132589ed29353c6fafdb884c5a6e04ea938834f0d9d"
+ ],
+ "signature": null,
+ "input": null
+ }
+ },
+ {
+ "description": "BIP341 Test case 7 - spend leaf 2",
+ "arguments": {
+ "internalPubkey": "55adf4e8967fbd2e29f20ac896e60c3b0f1d5b0efa9d34941b5958c7b0a0312d",
+ "redeem": {
+ "output": "c440b462ad48c7a77f94cd4532d8f2119dcebbd7c9764557e62726419b08ad4c OP_CHECKSIG",
+ "redeemVersion": 192
+ },
+ "scriptTree": [
+ {
+ "output": "71981521ad9fc9036687364118fb6ccd2035b96a423c59c5430e98310a11abe2 OP_CHECKSIG",
+ "version": 192
+ },
+ [
+ {
+ "output": "d5094d2dbe9b76e2c245a2b89b6006888952e2faa6a149ae318d69e520617748 OP_CHECKSIG",
+ "version": 192
+ },
+ {
+ "output": "c440b462ad48c7a77f94cd4532d8f2119dcebbd7c9764557e62726419b08ad4c OP_CHECKSIG",
+ "version": 192
+ }
+ ]
+ ]
+ },
+ "options": {},
+ "expected": {
+ "name": "p2tr",
+ "output": "OP_1 75169f4001aa68f15bbed28b218df1d0a62cbbcf1188c6665110c293c907b831",
+ "pubkey": "75169f4001aa68f15bbed28b218df1d0a62cbbcf1188c6665110c293c907b831",
+ "address": "bc1pw5tf7sqp4f50zka7629jrr036znzew70zxyvvej3zrpf8jg8hqcssyuewe",
+ "hash": "2f6b2c5397b6d68ca18e09a3f05161668ffe93a988582d55c6f07bd5b3329def",
+ "witness": [
+ "20c440b462ad48c7a77f94cd4532d8f2119dcebbd7c9764557e62726419b08ad4cac",
+ "c155adf4e8967fbd2e29f20ac896e60c3b0f1d5b0efa9d34941b5958c7b0a0312d737ed1fe30bc42b8022d717b44f0d93516617af64a64753b7a06bf16b26cd711f154e8e8e17c31d3462d7132589ed29353c6fafdb884c5a6e04ea938834f0d9d"
+ ],
+ "signature": null,
+ "input": null
+ }
+ }
+ ],
+ "invalid": [
+ {
+ "exception": "Not enough data",
+ "arguments": {}
+ },
+ {
+ "exception": "Not enough data",
+ "arguments": {
+ "signature": "300602010002010001"
+ }
+ },
+ {
+ "description": "Incorrect Witness Version",
+ "exception": "Output is invalid",
+ "arguments": {
+ "output": "OP_0 ab610d22c801def8a1e02368d1b92018970eb52a729919705e8a1a2f60c750f5"
+ }
+ },
+ {
+ "description": "Invalid x coordinate for pubkey in pubkey",
+ "exception": "Invalid pubkey for p2tr",
+ "arguments": {
+ "pubkey": "f136e956540197c21ff3c075d32a6e3c82f1ee1e646cc0f08f51b0b5edafa762"
+ }
+ },
+ {
+ "description": "Invalid x coordinate for pubkey in output",
+ "exception": "Invalid pubkey for p2tr",
+ "arguments": {
+ "output": "OP_1 f136e956540197c21ff3c075d32a6e3c82f1ee1e646cc0f08f51b0b5edafa762"
+ }
+ },
+ {
+ "description": "Invalid x coordinate for pubkey in address",
+ "exception": "Invalid pubkey for p2tr",
+ "arguments": {
+ "address": "bc1p7ymwj4j5qxtuy8lncp6ax2nw8jp0rms7v3kvpuy02xcttmd05a3qmwlnez"
+ }
+ },
+ {
+ "description": "Pubkey mismatch between pubkey and output",
+ "exception": "Pubkey mismatch",
+ "options": {},
+ "arguments": {
+ "pubkey": "ab610d22c801def8a1e02368d1b92018970eb52a729919705e8a1a2f60c750f5",
+ "output": "OP_1 12d7dac98d69a086a50b30959a3537950f356ffc6f50a263ab75c8a3ec9d44c1"
+ }
+ },
+ {
+ "description": "Pubkey mismatch between pubkey and address",
+ "exception": "Pubkey mismatch",
+ "options": {},
+ "arguments": {
+ "pubkey": "ab610d22c801def8a1e02368d1b92018970eb52a729919705e8a1a2f60c750f5",
+ "address": "bc1pztta4jvddxsgdfgtxz2e5dfhj58n2mludag2ycatwhy28myagnqsnl7mv7"
+ }
+ },
+ {
+ "description": "Pubkey mismatch between output and address",
+ "exception": "Pubkey mismatch",
+ "options": {},
+ "arguments": {
+ "output": "OP_1 ab610d22c801def8a1e02368d1b92018970eb52a729919705e8a1a2f60c750f5",
+ "address": "bc1pztta4jvddxsgdfgtxz2e5dfhj58n2mludag2ycatwhy28myagnqsnl7mv7"
+ }
+ },
+ {
+ "description": "Pubkey mismatch between internalPubkey and pubkey",
+ "exception": "Pubkey mismatch",
+ "options": {},
+ "arguments": {
+ "internalPubkey": "9fa5ffb68821cf559001caa0577eeea4978b29416def328a707b15e91701a2f7",
+ "pubkey": "ab610d22c801def8a1e02368d1b92018970eb52a729919705e8a1a2f60c750f5"
+ }
+ },
+ {
+ "description": "Hash mismatch between scriptTree and hash",
+ "exception": "Hash mismatch",
+ "options": {},
+ "arguments": {
+ "internalPubkey": "9fa5ffb68821cf559001caa0577eeea4978b29416def328a707b15e91701a2f7",
+ "scriptTree": {
+ "output": "83d8ee77a0f3a32a5cea96fd1624d623b836c1e5d1ac2dcde46814b619320c18 OP_CHECKSIG"
+ },
+ "hash": "b76077013c8e303085e300000000000000000000000000000000000000000000"
+ }
+ },
+ {
+ "exception": "Expected Point",
+ "options": {},
+ "arguments": {
+ "internalPubkey": "9fa5ffb68821cf559001caa0577eeea4978b29416def328a707b15e91701a2f8"
+ }
+ },
+ {
+ "exception": "Signature mismatch",
+ "arguments": {
+ "pubkey": "ab610d22c801def8a1e02368d1b92018970eb52a729919705e8a1a2f60c750f5",
+ "signature": "a251221c339a7129dd0b769698aca40d8d9da9570ab796a1820b91ab7dbf5acbea21c88ba8f1e9308a21729baf080734beaf97023882d972f75e380d480fd704",
+ "witness": [
+ "607b8b5b5c8614757736e3d5811790636d2a8e2ea14418f8cff66b2e898b3b7536a49b7c4bc8b3227953194bf5d0548e13e3526fdb36beeefadda1ec834a0db2"
+ ]
+ }
+ },
+ {
+ "exception": "Invalid prefix or Network mismatch",
+ "arguments": {
+ "address": "bcrt1prhepe49mpmhclwcqmkzpaz43revunykc7fc0f9az6pq08sn4qe7sxtrd8y"
+ }
+ },
+ {
+ "exception": "Invalid address version",
+ "arguments": {
+ "address": "bc1z4dss6gkgq8003g0qyd5drwfqrztsadf2w2v3juz73gdz7cx82r6s6rxhwd"
+ }
+ },
+ {
+ "exception": "Invalid address data",
+ "arguments": {
+ "address": "bc1p4dss6gkgq8003g0qyd5drwfqrztsadf2w2v3juz73gdz7cx82qh3d2w3"
+ }
+ },
+ {
+ "description": "Control block length too small",
+ "exception": "The control-block length is too small. Got 16, expected min 33.",
+ "arguments": {
+ "witness": [
+ "9675a9982c6398ea9d441cb7a943bcd6ff033cc3a2e01a0178a7d3be4575be863871c6bf3eef5ecd34721c784259385ca9101c3a313e010ac942c99de05aaaa602",
+ "5799cf4b193b730fb99580b186f7477c2cca4d28957326f6f1a5d14116438530e7ec0ce1cd465ad96968ae8a6a09d4d37a060a115919f56fcfebe7b2277cc2df5cc08fb6cda9105ee2512b2e22635aba",
+ "7520c7b5db9562078049719228db2ac80cb9643ec96c8055aa3b29c2c03d4d99edb0ac",
+ "c1a7957acbaaf7b444c53d9e0c9436e8"
+ ]
+ }
+ },
+ {
+ "description": "Control block must have a length of 33 + 32m (0 <= m <= 128)",
+ "exception": "The control-block length of 40 is incorrect!",
+ "arguments": {
+ "witness": [
+ "9675a9982c6398ea9d441cb7a943bcd6ff033cc3a2e01a0178a7d3be4575be863871c6bf3eef5ecd34721c784259385ca9101c3a313e010ac942c99de05aaaa602",
+ "5799cf4b193b730fb99580b186f7477c2cca4d28957326f6f1a5d14116438530e7ec0ce1cd465ad96968ae8a6a09d4d37a060a115919f56fcfebe7b2277cc2df5cc08fb6cda9105ee2512b2e22635aba",
+ "7520c7b5db9562078049719228db2ac80cb9643ec96c8055aa3b29c2c03d4d99edb0ac",
+ "c1a7957acbaaf7b444c53d9e0c9436e8a8a3247fd515095d66ddf6201918b40a3668f9a4ccdffcf77"
+ ]
+ }
+ },
+ {
+ "description": "Control block length too large",
+ "exception": "The script path is too long. Got 129, expected max 128.",
+ "arguments": {
+ "witness": [
+ "9675a9982c6398ea9d441cb7a943bcd6ff033cc3a2e01a0178a7d3be4575be863871c6bf3eef5ecd34721c784259385ca9101c3a313e010ac942c99de05aaaa602",
+ "5799cf4b193b730fb99580b186f7477c2cca4d28957326f6f1a5d14116438530e7ec0ce1cd465ad96968ae8a6a09d4d37a060a115919f56fcfebe7b2277cc2df5cc08fb6cda9105ee2512b2e22635aba",
+ "7520c7b5db9562078049719228db2ac80cb9643ec96c8055aa3b29c2c03d4d99edb0ac",
+ "c1a7957acbaaf7b444c53d9e0c9436e8a8a3247fd515095d66ddf6201918b40a3668f9a4ccdffcf778da624dca2dda0b08e763ec52fd4ad403ec7563a3504d0cc168b9a77a410029e01dac89567c9b2e6cd726e840351df3f2f58fefe976200a19244150d04153909f660184d656ee95fa7bf8e1d4ec83da1fca34f64bc279b76d257ec623e08baba2cfa4ea9e99646e88f1eb1668c00c0f15b7443c8ab83481611cc3ae85eb89a7bfc40067eb1d2e6354a32426d0ce710e88bc4cc0718b99c325509c9d02a6a980d675a8969be10ee9bef82cafee2fc913475667ccda37b1bc7f13f64e56c449c532658ba8481631c02ead979754c809584a875951619cec8fb040c33f06468ae0266cd8693d6a64cea5912be32d8de95a6da6300b0c50fdcd6001ea41126e7b7e5280d455054a816560028f5ca53c9a50ee52f10e15c5337315bad1f5277acb109a1418649dc6ead2fe14699742fee7182f2f15e54279c7d932ed2799d01d73c97e68bbc94d6f7f56ee0a80efd7c76e3169e10d1a1ba3b5f1eb02369dc43af687461c7a2a3344d13eb5485dca29a67f16b4cb988923060fd3b65d0f0352bb634bcc44f2fe668836dcd0f604150049835135dc4b4fbf90fb334b3938a1f137eb32f047c65b85e6c1173b890b6d0162b48b186d1f1af8521945924ac8ac8efec321bf34f1d4b3d4a304a10313052c652d53f6ecb8a55586614e8950cde9ab6fe8e22802e93b3b9139112250b80ebc589aba231af535bb20f7eeec2e412f698c17f3fdc0a2e20924a5e38b21a628a9e3b2a61e35958e60c7f5087c50cde9ab6fe8e22802e93b3b9139112250b80ebc589aba231af535bb20f7eeec2e412f698c17f3fdc0a2e20924a5e38b21a628a9e3b2a61e35958e60c7f5087c50cde9ab6fe8e22802e93b3b9139112250b80ebc589aba231af535bb20f7eeec2e412f698c17f3fdc0a2e20924a5e38b21a628a9e3b2a61e35958e60c7f5087c50cde9ab6fe8e22802e93b3b9139112250b80ebc589aba231af535bb20f7eeec2e412f698c17f3fdc0a2e20924a5e38b21a628a9e3b2a61e35958e60c7f5087c50cde9ab6fe8e22802e93b3b9139112250b80ebc589aba231af535bb20f7eeec2e412f698c17f3fdc0a2e20924a5e38b21a628a9e3b2a61e35958e60c7f5087c50cde9ab6fe8e22802e93b3b9139112250b80ebc589aba231af535bb20f7eeec2e412f698c17f3fdc0a2e20924a5e38b21a628a9e3b2a61e35958e60c7f5087c50cde9ab6fe8e22802e93b3b9139112250b80ebc589aba231af535bb20f7eeec2e412f698c17f3fdc0a2e20924a5e38b21a628a9e3b2a61e35958e60c7f5087c50cde9ab6fe8e22802e93b3b9139112250b80ebc589aba231af535bb20f7eeec2e412f698c17f3fdc0a2e20924a5e38b21a628a9e3b2a61e35958e60c7f5087c50cde9ab6fe8e22802e93b3b9139112250b80ebc589aba231af535bb20f7eeec2e412f698c17f3fdc0a2e20924a5e38b21a628a9e3b2a61e35958e60c7f5087c50cde9ab6fe8e22802e93b3b9139112250b80ebc589aba231af535bb20f7eeec2e412f698c17f3fdc0a2e20924a5e38b21a628a9e3b2a61e35958e60c7f5087c50cde9ab6fe8e22802e93b3b9139112250b80ebc589aba231af535bb20f7eeec2e412f698c17f3fdc0a2e20924a5e38b21a628a9e3b2a61e35958e60c7f5087c50cde9ab6fe8e22802e93b3b9139112250b80ebc589aba231af535bb20f7eeec2e412f698c17f3fdc0a2e20924a5e38b21a628a9e3b2a61e35958e60c7f5087c50cde9ab6fe8e22802e93b3b9139112250b80ebc589aba231af535bb20f7eeec2e412f698c17f3fdc0a2e20924a5e38b21a628a9e3b2a61e35958e60c7f5087c50cde9ab6fe8e22802e93b3b9139112250b80ebc589aba231af535bb20f7eeec2e412f698c17f3fdc0a2e20924a5e38b21a628a9e3b2a61e35958e60c7f5087c50cde9ab6fe8e22802e93b3b9139112250b80ebc589aba231af535bb20f7eeec2e412f698c17f3fdc0a2e20924a5e38b21a628a9e3b2a61e35958e60c7f5087c50cde9ab6fe8e22802e93b3b9139112250b80ebc589aba231af535bb20f7eeec2e412f698c17f3fdc0a2e20924a5e38b21a628a9e3b2a61e35958e60c7f5087c50cde9ab6fe8e22802e93b3b9139112250b80ebc589aba231af535bb20f7eeec2e412f698c17f3fdc0a2e20924a5e38b21a628a9e3b2a61e35958e60c7f5087c50cde9ab6fe8e22802e93b3b9139112250b80ebc589aba231af535bb20f7eeec2e412f698c17f3fdc0a2e20924a5e38b21a628a9e3b2a61e35958e60c7f5087c50cde9ab6fe8e22802e93b3b9139112250b80ebc589aba231af535bb20f7eeec2e412f698c17f3fdc0a2e20924a5e38b21a628a9e3b2a61e35958e60c7f5087c50cde9ab6fe8e22802e93b3b9139112250b80ebc589aba231af535bb20f7eeec2e412f698c17f3fdc0a2e20924a5e38b21a628a9e3b2a61e35958e60c7f5087c50cde9ab6fe8e22802e93b3b9139112250b80ebc589aba231af535bb20f7eeec2e412f698c17f3fdc0a2e20924a5e38b21a628a9e3b2a61e35958e60c7f5087c50cde9ab6fe8e22802e93b3b9139112250b80ebc589aba231af535bb20f7eeec2e412f698c17f3fdc0a2e20924a5e38b21a628a9e3b2a61e35958e60c7f5087c50cde9ab6fe8e22802e93b3b9139112250b80ebc589aba231af535bb20f7eeec2e412f698c17f3fdc0a2e20924a5e38b21a628a9e3b2a61e35958e60c7f5087c50cde9ab6fe8e22802e93b3b9139112250b80ebc589aba231af535bb20f7eeec2e412f698c17f3fdc0a2e20924a5e38b21a628a9e3b2a61e35958e60c7f5087c50cde9ab6fe8e22802e93b3b9139112250b80ebc589aba231af535bb20f7eeec2e412f698c17f3fdc0a2e20924a5e38b21a628a9e3b2a61e35958e60c7f5087c50cde9ab6fe8e22802e93b3b9139112250b80ebc589aba231af535bb20f7eeec2e412f698c17f3fdc0a2e20924a5e38b21a628a9e3b2a61e35958e60c7f5087c50cde9ab6fe8e22802e93b3b9139112250b80ebc589aba231af535bb20f7eeec2e412f698c17f3fdc0a2e20924a5e38b21a628a9e3b2a61e35958e60c7f5087c50cde9ab6fe8e22802e93b3b9139112250b80ebc589aba231af535bb20f7eeec2e412f698c17f3fdc0a2e20924a5e38b21a628a9e3b2a61e35958e60c7f5087c50cde9ab6fe8e22802e93b3b9139112250b80ebc589aba231af535bb20f7eeec2e412f698c17f3fdc0a2e20924a5e38b21a628a9e3b2a61e35958e60c7f5087c50cde9ab6fe8e22802e93b3b9139112250b80ebc589aba231af535bb20f7eeec2e412f698c17f3fdc0a2e20924a5e38b21a628a9e3b2a61e35958e60c7f5087c50cde9ab6fe8e22802e93b3b9139112250b80ebc589aba231af535bb20f7eeec2e412f698c17f3fdc0a2e20924a5e38b21a628a9e3b2a61e35958e60c7f5087c50cde9ab6fe8e22802e93b3b9139112250b80ebc589aba231af535bb20f7eeec2e412f698c17f3fdc0a2e20924a5e38b21a628a9e3b2a61e35958e60c7f5087c50cde9ab6fe8e22802e93b3b9139112250b80ebc589aba231af535bb20f7eeec2e412f698c17f3fdc0a2e20924a5e38b21a628a9e3b2a61e35958e60c7f5087c50cde9ab6fe8e22802e93b3b9139112250b80ebc589aba231af535bb20f7eeec2e412f698c17f3fdc0a2e20924a5e38b21a628a9e3b2a61e35958e60c7f5087c50cde9ab6fe8e22802e93b3b9139112250b80ebc589aba231af535bb20f7eeec2e412f698c17f3fdc0a2e20924a5e38b21a628a9e3b2a61e35958e60c7f5087c50cde9ab6fe8e22802e93b3b9139112250b80ebc589aba231af535bb20f7eeec2e412f698c17f3fdc0a2e20924a5e38b21a628a9e3b2a61e35958e60c7f5087c50cde9ab6fe8e22802e93b3b9139112250b80ebc589aba231af535bb20f7eeec2e412f698c17f3fdc0a2e20924a5e38b21a628a9e3b2a61e35958e60c7f5087c50cde9ab6fe8e22802e93b3b9139112250b80ebc589aba231af535bb20f7eeec2e412f698c17f3fdc0a2e20924a5e38b21a628a9e3b2a61e35958e60c7f5087c50cde9ab6fe8e22802e93b3b9139112250b80ebc589aba231af535bb20f7eeec2e412f698c17f3fdc0a2e20924a5e38b21a628a9e3b2a61e35958e60c7f5087c50cde9ab6fe8e22802e93b3b9139112250b80ebc589aba231af535bb20f7eeec2e412f698c17f3fdc0a2e20924a5e38b21a628a9e3b2a61e35958e60c7f5087c50cde9ab6fe8e22802e93b3b9139112250b80ebc589aba231af535bb20f7eeec2e412f698c17f3fdc0a2e20924a5e38b21a628a9e3b2a61e35958e60c7f5087c50cde9ab6fe8e22802e93b3b9139112250b80ebc589aba231af535bb20f7eeec2e412f698c17f3fdc0a2e20924a5e38b21a628a9e3b2a61e35958e60c7f5087c50cde9ab6fe8e22802e93b3b9139112250b80ebc589aba231af535bb20f7eeec2e412f698c17f3fdc0a2e20924a5e38b21a628a9e3b2a61e35958e60c7f5087c50cde9ab6fe8e22802e93b3b9139112250b80ebc589aba231af535bb20f7eeec2e412f698c17f3fdc0a2e20924a5e38b21a628a9e3b2a61e35958e60c7f5087c50cde9ab6fe8e22802e93b3b9139112250b80ebc589aba231af535bb20f7eeec2e412f698c17f3fdc0a2e20924a5e38b21a628a9e3b2a61e35958e60c7f5087c50cde9ab6fe8e22802e93b3b9139112250b80ebc589aba231af535bb20f7eeec2e412f698c17f3fdc0a2e20924a5e38b21a628a9e3b2a61e35958e60c7f5087c50cde9ab6fe8e22802e93b3b9139112250b80ebc589aba231af535bb20f7eeec2e412f698c17f3fdc0a2e20924a5e38b21a628a9e3b2a61e35958e60c7f5087c50cde9ab6fe8e22802e93b3b9139112250b80ebc589aba231af535bb20f7eeec2e412f698c17f3fdc0a2e20924a5e38b21a628a9e3b2a61e35958e60c7f5087c50cde9ab6fe8e22802e93b3b9139112250b80ebc589aba231af535bb20f7eeec2e412f698c17f3fdc0a2e20924a5e38b21a628a9e3b2a61e35958e60c7f5087c50cde9ab6fe8e22802e93b3b9139112250b80ebc589aba231af535bb20f7eeec2e412f698c17f3fdc0a2e20924a5e38b21a628a9e3b2a61e35958e60c7f5087c50cde9ab6fe8e22802e93b3b9139112250b80ebc589aba231af535bb20f7eeec2e412f698c17f3fdc0a2e20924a5e38b21a628a9e3b2a61e35958e60c7f5087c50cde9ab6fe8e22802e93b3b9139112250b80ebc589aba231af535bb20f7eeec2e412f698c17f3fdc0a2e20924a5e38b21a628a9e3b2a61e35958e60c7f5087c50cde9ab6fe8e22802e93b3b9139112250b80ebc589aba231af535bb20f7eeec2e412f698c17f3fdc0a2e20924a5e38b21a628a9e3b2a61e35958e60c7f5087c50cde9ab6fe8e22802e93b3b9139112250b80ebc589aba231af535bb20f7eeec2e412f698c17f3fdc0a2e20924a5e38b21a628a9e3b2a61e35958e60c7f5087c50cde9ab6fe8e22802e93b3b9139112250b80ebc589aba231af535bb20f7eeec2e412f698c17f3fdc0a2e20924a5e38b21a628a9e3b2a61e35958e60c7f5087c50cde9ab6fe8e22802e93b3b9139112250b80ebc589aba231af535bb20f7eeec2e412f698c17f3fdc0a2e20924a5e38b21a628a9e3b2a61e35958e60c7f5087c50cde9ab6fe8e22802e93b3b9139112250b80ebc589aba231af535bb20f7eeec2e412f698c17f3fdc0a2e20924a5e38b21a628a9e3b2a61e35958e60c7f5087c"
+ ]
+ }
+ },
+ {
+ "description": "Invalid internalPubkey in control block",
+ "exception": "Invalid internalPubkey for p2tr witness",
+ "arguments": {
+ "witness": [
+ "9675a9982c6398ea9d441cb7a943bcd6ff033cc3a2e01a0178a7d3be4575be863871c6bf3eef5ecd34721c784259385ca9101c3a313e010ac942c99de05aaaa602",
+ "5799cf4b193b730fb99580b186f7477c2cca4d28957326f6f1a5d14116438530e7ec0ce1cd465ad96968ae8a6a09d4d37a060a115919f56fcfebe7b2277cc2df5cc08fb6cda9105ee2512b2e22635aba",
+ "7520c7b5db9562078049719228db2ac80cb9643ec96c8055aa3b29c2c03d4d99edb0ac",
+ "c14444444444444444453d9e0c9436e8a8a3247fd515095d66ddf6201918b40a3668f9a4ccdffcf778da624dca2dda0b08e763ec52fd4ad403ec7563a3504d0cc168b9a77a410029e01dac89567c9b2e6cd726e840351df3f2f58fefe976200a19244150d04153909f660184d656ee95fa7bf8e1d4ec83da1fca34f64bc279b76d257ec623e08baba2cfa4ea9e99646e88f1eb1668c00c0f15b7443c8ab83481611cc3ae85eb89a7bfc40067eb1d2e6354a32426d0ce710e88bc4cc0718b99c325509c9d02a6a980d675a8969be10ee9bef82cafee2fc913475667ccda37b1bc7f13f64e56c449c532658ba8481631c02ead979754c809584a875951619cec8fb040c33f06468ae0266cd8693d6a64cea5912be32d8de95a6da6300b0c50fdcd6001ea41126e7b7e5280d455054a816560028f5ca53c9a50ee52f10e15c5337315bad1f5277acb109a1418649dc6ead2fe14699742fee7182f2f15e54279c7d932ed2799d01d73c97e68bbc94d6f7f56ee0a80efd7c76e3169e10d1a1ba3b5f1eb02369dc43af687461c7a2a3344d13eb5485dca29a67f16b4cb988923060fd3b65d0f0352bb634bcc44f2fe668836dcd0f604150049835135dc4b4fbf90fb334b3938a1f137eb32f047c65b85e6c1173b890b6d0162b48b186d1f1af8521945924ac8ac8efec321bf34f1d4b3d4a304a10313052c652d53f6ecb8a55586614e8950cde9ab6fe8e22802e93b3b9139112250b80ebc589aba231af535bb20f7eeec2e412f698c17f3fdc0a2e20924a5e38b21a628a9e3b2a61e35958e60c7f5087c"
+ ]
+ }
+ },
+ {
+ "description": "internalPubkey mismatch between control block and internalKey",
+ "exception": "Internal pubkey mismatch",
+ "arguments": {
+ "internalPubkey": "9fa5ffb68821cf559001caa0577eeea4978b29416def328a707b15e91701a2f7",
+ "witness": [
+ "9675a9982c6398ea9d441cb7a943bcd6ff033cc3a2e01a0178a7d3be4575be863871c6bf3eef5ecd34721c784259385ca9101c3a313e010ac942c99de05aaaa602",
+ "5799cf4b193b730fb99580b186f7477c2cca4d28957326f6f1a5d14116438530e7ec0ce1cd465ad96968ae8a6a09d4d37a060a115919f56fcfebe7b2277cc2df5cc08fb6cda9105ee2512b2e22635aba",
+ "7520c7b5db9562078049719228db2ac80cb9643ec96c8055aa3b29c2c03d4d99edb0ac",
+ "c1a7957acbaaf7b444c53d9e0c9436e8a8a3247fd515095d66ddf6201918b40a3668f9a4ccdffcf778da624dca2dda0b08e763ec52fd4ad403ec7563a3504d0cc168b9a77a410029e01dac89567c9b2e6cd726e840351df3f2f58fefe976200a19244150d04153909f660184d656ee95fa7bf8e1d4ec83da1fca34f64bc279b76d257ec623e08baba2cfa4ea9e99646e88f1eb1668c00c0f15b7443c8ab83481611cc3ae85eb89a7bfc40067eb1d2e6354a32426d0ce710e88bc4cc0718b99c325509c9d02a6a980d675a8969be10ee9bef82cafee2fc913475667ccda37b1bc7f13f64e56c449c532658ba8481631c02ead979754c809584a875951619cec8fb040c33f06468ae0266cd8693d6a64cea5912be32d8de95a6da6300b0c50fdcd6001ea41126e7b7e5280d455054a816560028f5ca53c9a50ee52f10e15c5337315bad1f5277acb109a1418649dc6ead2fe14699742fee7182f2f15e54279c7d932ed2799d01d73c97e68bbc94d6f7f56ee0a80efd7c76e3169e10d1a1ba3b5f1eb02369dc43af687461c7a2a3344d13eb5485dca29a67f16b4cb988923060fd3b65d0f0352bb634bcc44f2fe668836dcd0f604150049835135dc4b4fbf90fb334b3938a1f137eb32f047c65b85e6c1173b890b6d0162b48b186d1f1af8521945924ac8ac8efec321bf34f1d4b3d4a304a10313052c652d53f6ecb8a55586614e8950cde9ab6fe8e22802e93b3b9139112250b80ebc589aba231af535bb20f7eeec2e412f698c17f3fdc0a2e20924a5e38b21a628a9e3b2a61e35958e60c7f5087c"
+ ]
+ }
+ },
+ {
+ "description": "pubkey mismatch between outputKey and pubkey",
+ "exception": "Pubkey mismatch for p2tr witness",
+ "arguments": {
+ "pubkey": "df0e070ca2fca05ecd191bdba047841d62414b2bdcb6249c258fd64c0dd251ff",
+ "witness": [
+ "9675a9982c6398ea9d441cb7a943bcd6ff033cc3a2e01a0178a7d3be4575be863871c6bf3eef5ecd34721c784259385ca9101c3a313e010ac942c99de05aaaa602",
+ "5799cf4b193b730fb99580b186f7477c2cca4d28957326f6f1a5d14116438530e7ec0ce1cd465ad96968ae8a6a09d4d37a060a115919f56fcfebe7b2277cc2df5cc08fb6cda9105ee2512b2e22635aba",
+ "7520c7b5db9562078049719228db2ac80cb9643ec96c8055aa3b29c2c03d4d99edb0ac",
+ "c1a7957acbaaf7b444c53d9e0c9436e8a8a3247fd515095d66ddf6201918b40a3668f9a4ccdffcf778da624dca2dda0b08e763ec52fd4ad403ec7563a3504d0cc168b9a77a410029e01dac89567c9b2e6cd726e840351df3f2f58fefe976200a19244150d04153909f660184d656ee95fa7bf8e1d4ec83da1fca34f64bc279b76d257ec623e08baba2cfa4ea9e99646e88f1eb1668c00c0f15b7443c8ab83481611cc3ae85eb89a7bfc40067eb1d2e6354a32426d0ce710e88bc4cc0718b99c325509c9d02a6a980d675a8969be10ee9bef82cafee2fc913475667ccda37b1bc7f13f64e56c449c532658ba8481631c02ead979754c809584a875951619cec8fb040c33f06468ae0266cd8693d6a64cea5912be32d8de95a6da6300b0c50fdcd6001ea41126e7b7e5280d455054a816560028f5ca53c9a50ee52f10e15c5337315bad1f5277acb109a1418649dc6ead2fe14699742fee7182f2f15e54279c7d932ed2799d01d73c97e68bbc94d6f7f56ee0a80efd7c76e3169e10d1a1ba3b5f1eb02369dc43af687461c7a2a3344d13eb5485dca29a67f16b4cb988923060fd3b65d0f0352bb634bcc44f2fe668836dcd0f604150049835135dc4b4fbf90fb334b3938a1f137eb32f047c65b85e6c1173b890b6d0162b48b186d1f1af8521945924ac8ac8efec321bf34f1d4b3d4a304a10313052c652d53f6ecb8a55586614e8950cde9ab6fe8e22802e93b3b9139112250b80ebc589aba231af535bb20f7eeec2e412f698c17f3fdc0a2e20924a5e38b21a628a9e3b2a61e35958e60c7f5087c"
+ ]
+ }
+ },
+ {
+ "description": "parity",
+ "exception": "Incorrect parity",
+ "arguments": {
+ "witness": [
+ "9675a9982c6398ea9d441cb7a943bcd6ff033cc3a2e01a0178a7d3be4575be863871c6bf3eef5ecd34721c784259385ca9101c3a313e010ac942c99de05aaaa602",
+ "5799cf4b193b730fb99580b186f7477c2cca4d28957326f6f1a5d14116438530e7ec0ce1cd465ad96968ae8a6a09d4d37a060a115919f56fcfebe7b2277cc2df5cc08fb6cda9105ee2512b2e22635aba",
+ "7520c7b5db9562078049719228db2ac80cb9643ec96c8055aa3b29c2c03d4d99edb0ac",
+ "c0a7957acbaaf7b444c53d9e0c9436e8a8a3247fd515095d66ddf6201918b40a3668f9a4ccdffcf778da624dca2dda0b08e763ec52fd4ad403ec7563a3504d0cc168b9a77a410029e01dac89567c9b2e6cd726e840351df3f2f58fefe976200a19244150d04153909f660184d656ee95fa7bf8e1d4ec83da1fca34f64bc279b76d257ec623e08baba2cfa4ea9e99646e88f1eb1668c00c0f15b7443c8ab83481611cc3ae85eb89a7bfc40067eb1d2e6354a32426d0ce710e88bc4cc0718b99c325509c9d02a6a980d675a8969be10ee9bef82cafee2fc913475667ccda37b1bc7f13f64e56c449c532658ba8481631c02ead979754c809584a875951619cec8fb040c33f06468ae0266cd8693d6a64cea5912be32d8de95a6da6300b0c50fdcd6001ea41126e7b7e5280d455054a816560028f5ca53c9a50ee52f10e15c5337315bad1f5277acb109a1418649dc6ead2fe14699742fee7182f2f15e54279c7d932ed2799d01d73c97e68bbc94d6f7f56ee0a80efd7c76e3169e10d1a1ba3b5f1eb02369dc43af687461c7a2a3344d13eb5485dca29a67f16b4cb988923060fd3b65d0f0352bb634bcc44f2fe668836dcd0f604150049835135dc4b4fbf90fb334b3938a1f137eb32f047c65b85e6c1173b890b6d0162b48b186d1f1af8521945924ac8ac8efec321bf34f1d4b3d4a304a10313052c652d53f6ecb8a55586614e8950cde9ab6fe8e22802e93b3b9139112250b80ebc589aba231af535bb20f7eeec2e412f698c17f3fdc0a2e20924a5e38b21a628a9e3b2a61e35958e60c7f5087c"
+ ]
+ }
+ },
+ {
+ "description": "Script Tree is not a binary tree (has tree leafs)",
+ "exception": "ValiError: Taptree is not of type isTaptree",
+ "options": {},
+ "arguments": {
+ "internalPubkey": "9fa5ffb68821cf559001caa0577eeea4978b29416def328a707b15e91701a2f7",
+ "scriptTree": [
+ {
+ "output": "71981521ad9fc9036687364118fb6ccd2035b96a423c59c5430e98310a11abe2 OP_CHECKSIG",
+ "version": 192
+ },
+ [
+ {
+ "output": "d5094d2dbe9b76e2c245a2b89b6006888952e2faa6a149ae318d69e520617748 OP_CHECKSIG",
+ "version": 192
+ },
+ {
+ "output": "c440b462ad48c7a77f94cd4532d8f2119dcebbd7c9764557e62726419b08ad4c OP_CHECKSIG",
+ "version": 192
+ },
+ {
+ "output": "c440b462ad48c7a77f94cd4532d8f2119dcebbd7c9764557e62726419b08ad4c OP_CHECKSIG",
+ "version": 192
+ }
+ ]
+ ],
+ "hash": "b76077013c8e303085e300000000000000000000000000000000000000000000"
+ }
+ },
+ {
+ "description": "Script Tree is not a TapTree tree (leaf has no script)",
+ "exception": "ValiError: Taptree is not of type isTaptree",
+ "options": {},
+ "arguments": {
+ "internalPubkey": "9fa5ffb68821cf559001caa0577eeea4978b29416def328a707b15e91701a2f7",
+ "scriptTree": [
+ {
+ "output": "71981521ad9fc9036687364118fb6ccd2035b96a423c59c5430e98310a11abe2 OP_CHECKSIG",
+ "version": 192
+ },
+ [
+ [
+ [
+ [
+ {
+ "output": "d5094d2dbe9b76e2c245a2b89b6006888952e2faa6a149ae318d69e520617748 OP_CHECKSIG",
+ "version": 192
+ },
+ {
+ "version": 192
+ }
+ ]
+ ]
+ ]
+ ]
+ ],
+ "hash": "b76077013c8e303085e300000000000000000000000000000000000000000000"
+ }
+ },
+ {
+ "description": "Incorrect redeem version",
+ "exception": "Redeem.redeemVersion and witness mismatch",
+ "arguments": {
+ "witness": [
+ "20d85a959b0290bf19bb89ed43c916be835475d013da4b362117393e25a48229b8ac",
+ "c1187791b6f712a8ea41c8ecdd0ee77fab3e85263b37e1ec18a3651926b3a6cf27"
+ ],
+ "redeem": {
+ "output": "d85a959b0290bf19bb89ed43c916be835475d013da4b362117393e25a48229b8 OP_CHECKSIG",
+ "redeemVersion": 111
+ }
+ }
+ },
+ {
+ "description": "Incorrect redeem output",
+ "exception": "Redeem.output and witness mismatch",
+ "arguments": {
+ "witness": [
+ "20d85a959b0290bf19bb89ed43c916be835475d013da4b362117393e25a48229b8ac",
+ "c1187791b6f712a8ea41c8ecdd0ee77fab3e85263b37e1ec18a3651926b3a6cf27"
+ ],
+ "redeem": {
+ "output": "d85a959b0290bf19bb89ed43c916be835475d013da4b362117393e0000000000 OP_CHECKSIG",
+ "redeemVersion": 192
+ }
+ }
+ },
+ {
+ "description": "Incorrect redeem witness",
+ "exception": "Redeem.witness and witness mismatch",
+ "arguments": {
+ "witness": [
+ "9675a9982c6398ea9d441cb7a943bcd6ff033cc3a2e01a0178a7d3be4575be863871c6bf3eef5ecd34721c784259385ca9101c3a313e010ac942c99de05aaaa602",
+ "5799cf4b193b730fb99580b186f7477c2cca4d28957326f6f1a5d14116438530e7ec0ce1cd465ad96968ae8a6a09d4d37a060a115919f56fcfebe7b2277cc2df5cc08fb6cda9105ee2512b2e22635aba",
+ "7520c7b5db9562078049719228db2ac80cb9643ec96c8055aa3b29c2c03d4d99edb0ac",
+ "c1a7957acbaaf7b444c53d9e0c9436e8a8a3247fd515095d66ddf6201918b40a3668f9a4ccdffcf778da624dca2dda0b08e763ec52fd4ad403ec7563a3504d0cc168b9a77a410029e01dac89567c9b2e6cd726e840351df3f2f58fefe976200a19244150d04153909f660184d656ee95fa7bf8e1d4ec83da1fca34f64bc279b76d257ec623e08baba2cfa4ea9e99646e88f1eb1668c00c0f15b7443c8ab83481611cc3ae85eb89a7bfc40067eb1d2e6354a32426d0ce710e88bc4cc0718b99c325509c9d02a6a980d675a8969be10ee9bef82cafee2fc913475667ccda37b1bc7f13f64e56c449c532658ba8481631c02ead979754c809584a875951619cec8fb040c33f06468ae0266cd8693d6a64cea5912be32d8de95a6da6300b0c50fdcd6001ea41126e7b7e5280d455054a816560028f5ca53c9a50ee52f10e15c5337315bad1f5277acb109a1418649dc6ead2fe14699742fee7182f2f15e54279c7d932ed2799d01d73c97e68bbc94d6f7f56ee0a80efd7c76e3169e10d1a1ba3b5f1eb02369dc43af687461c7a2a3344d13eb5485dca29a67f16b4cb988923060fd3b65d0f0352bb634bcc44f2fe668836dcd0f604150049835135dc4b4fbf90fb334b3938a1f137eb32f047c65b85e6c1173b890b6d0162b48b186d1f1af8521945924ac8ac8efec321bf34f1d4b3d4a304a10313052c652d53f6ecb8a55586614e8950cde9ab6fe8e22802e93b3b9139112250b80ebc589aba231af535bb20f7eeec2e412f698c17f3fdc0a2e20924a5e38b21a628a9e3b2a61e35958e60c7f5087c"
+ ],
+ "redeem" : {
+ "output": "OP_DROP c7b5db9562078049719228db2ac80cb9643ec96c8055aa3b29c2c03d4d99edb0 OP_CHECKSIG",
+ "redeemVersion": 192,
+ "witness": [
+ "9675a9982c6398ea9d441cb7a943bcd6ff033cc3a2e01a0178a7d3be4575be863871c6bf3eef5ecd34721c784259385ca9101c3a313e0100000000000000000000",
+ "5799cf4b193b730fb99580b186f7477c2cca4d28957326f6f1a5d14116438530e7ec0ce1cd465ad96968ae8a6a09d4d37a060a115919f56fcfebe7b2277cc2df5cc08fb6cda9105ee2512b2e22635aba"
+ ]
+ }
+ }
+ },
+ {
+ "description": "Incorrect redeem output ASM",
+ "exception": "Redeem.output is invalid",
+ "arguments": {
+ "witness": [
+ "9675a9982c6398ea9d441cb7a943bcd6ff033cc3a2e01a0178a7d3be4575be863871c6bf3eef5ecd34721c784259385ca9101c3a313e010ac942c99de05aaaa602",
+ "5799cf4b193b730fb99580b186f7477c2cca4d28957326f6f1a5d14116438530e7ec0ce1cd465ad96968ae8a6a09d4d37a060a115919f56fcfebe7b2277cc2df5cc08fb6cda9105ee2512b2e22635aba",
+ "7520c7b5db9562078049719228db2ac80cb9643ec96c8055aa3b29c2c03d4d99edb0ac",
+ "c1a7957acbaaf7b444c53d9e0c9436e8a8a3247fd515095d66ddf6201918b40a3668f9a4ccdffcf778da624dca2dda0b08e763ec52fd4ad403ec7563a3504d0cc168b9a77a410029e01dac89567c9b2e6cd726e840351df3f2f58fefe976200a19244150d04153909f660184d656ee95fa7bf8e1d4ec83da1fca34f64bc279b76d257ec623e08baba2cfa4ea9e99646e88f1eb1668c00c0f15b7443c8ab83481611cc3ae85eb89a7bfc40067eb1d2e6354a32426d0ce710e88bc4cc0718b99c325509c9d02a6a980d675a8969be10ee9bef82cafee2fc913475667ccda37b1bc7f13f64e56c449c532658ba8481631c02ead979754c809584a875951619cec8fb040c33f06468ae0266cd8693d6a64cea5912be32d8de95a6da6300b0c50fdcd6001ea41126e7b7e5280d455054a816560028f5ca53c9a50ee52f10e15c5337315bad1f5277acb109a1418649dc6ead2fe14699742fee7182f2f15e54279c7d932ed2799d01d73c97e68bbc94d6f7f56ee0a80efd7c76e3169e10d1a1ba3b5f1eb02369dc43af687461c7a2a3344d13eb5485dca29a67f16b4cb988923060fd3b65d0f0352bb634bcc44f2fe668836dcd0f604150049835135dc4b4fbf90fb334b3938a1f137eb32f047c65b85e6c1173b890b6d0162b48b186d1f1af8521945924ac8ac8efec321bf34f1d4b3d4a304a10313052c652d53f6ecb8a55586614e8950cde9ab6fe8e22802e93b3b9139112250b80ebc589aba231af535bb20f7eeec2e412f698c17f3fdc0a2e20924a5e38b21a628a9e3b2a61e35958e60c7f5087c"
+ ],
+ "redeem" : {
+ "output": "",
+ "redeemVersion": 192,
+ "witness": [
+ "9675a9982c6398ea9d441cb7a943bcd6ff033cc3a2e01a0178a7d3be4575be863871c6bf3eef5ecd34721c784259385ca9101c3a313e0100000000000000000000",
+ "5799cf4b193b730fb99580b186f7477c2cca4d28957326f6f1a5d14116438530e7ec0ce1cd465ad96968ae8a6a09d4d37a060a115919f56fcfebe7b2277cc2df5cc08fb6cda9105ee2512b2e22635aba"
+ ]
+ }
+ }
+ },
+ {
+ "description": "Redeem script not in tree",
+ "exception": "Redeem script not in tree",
+ "options": {},
+ "arguments": {
+ "internalPubkey": "9fa5ffb68821cf559001caa0577eeea4978b29416def328a707b15e91701a2f7",
+ "scriptTree": {
+ "output": "83d8ee77a0f3a32a5cea96fd1624d623b836c1e5d1ac2dcde46814b619320c18 OP_CHECKSIG"
+ },
+ "redeem": {
+ "output": "83d8ee77a0f3a32a5cea96fd1624d623b836c1e5d1ac2dcde46814b619320c19 OP_CHECKSIG"
+ }
+ }
+ }
+ ],
+ "dynamic": {
+ "depends": {},
+ "details": []
+ }
+}
diff --git a/test/fixtures/p2wsh.json b/test/fixtures/p2wsh.json
index 03fb01d0f..effe4d148 100644
--- a/test/fixtures/p2wsh.json
+++ b/test/fixtures/p2wsh.json
@@ -264,6 +264,22 @@
}
}
},
+ {
+ "exception": "Redeem.output unspendable with more than 201 non-push ops",
+ "arguments": {
+ "redeem": {
+ "output": "OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP OP_0 OP_0 OP_2DROP 02c56aef124aa09836590fb858ce3517bf9d4b27e9d3fc81c2b00845f98ef87d9e OP_CHECKSIG"
+ }
+ }
+ },
+ {
+ "exception": "Redeem.output unspendable if larger than 3600 bytes",
+ "arguments": {
+ "redeem": {
+ "output": "02dfec67b1047f5ee7fcfc470565e07ce346fb89013f8e56b9e48a9af572f6cc9e OP_CHECKSIGVERIFY 034896ca85df092ac577480101193980c02b96a01533e7096858e696789c55b032 OP_CHECKSIGVERIFY 03365a5bd77cb355a5edefe0b47b64adf466d37c826c42332c8ea3bd2cb0fd2c0f OP_CHECKSIGVERIFY 0206c24e25bc5048bbb4723d701532db94366aa12bd080a8cf2cd35b41ea16b7e2 OP_CHECKSIGVERIFY 02e55fb5431a1c654cce06255befb4232600a94c4db57b4ab218a4ce04259c1145 OP_CHECKSIGVERIFY 02b4af93ac624e0f45e8577d828d0de343c595b9328958005baf6ab21a650ad39a OP_CHECKSIGVERIFY 03311744aa2a0d9b0af1c10f2eb4f4dd98b672fd61a575720298f334ebff3633fb OP_CHECKSIGVERIFY 0355b2ca92e66c250fafd45f3177b4a0fb48befc8b5911a37cdf3de5acc23b6e48 OP_CHECKSIGVERIFY 023e24b1d99ee02f08451af9e7e5d08db66db3e4ba04238bb4a484fc1bd66e4b89 OP_CHECKSIGVERIFY 03e0ece48ceb3c3bb566acbfda433662d2ce14f0b23bda0d0f065c5e0589ec8f1d OP_CHECKSIGVERIFY 02426aed062674081fe6e71ed130fc9b29329cef01204fe26b5ace638648b8d846 OP_CHECKSIGVERIFY 022960bc5bb274d8c963dbe6f77ef2cf59644f93f2dc3bf0d0036903656deaf0f3 OP_CHECKSIGVERIFY 03460e475689d3222710af5d3b8079bad3a12b248f61b300657b026c44c2b392b2 OP_CHECKSIGVERIFY 029d23671b573cbfb6272f5d197bbb88afa8fda9947affb5686c16a3e1f7592bb4 OP_CHECKSIGVERIFY 038bd4adeddfe560f8d9b1d1b7fc655c74fe605de09195c3f71e2fb8f68ecc4ca0 OP_CHECKSIGVERIFY 03a531c8795353c2a10f0e1619db468e5a87fd3303091662193c2701cfc1b1c4b0 OP_CHECKSIGVERIFY 02900a11b6b8ccc281dd7f1ab84b18358bfe2fef972967f0ca0ff1c71c5c72a8cb OP_CHECKSIGVERIFY 025e8d845e3f4d0d3afccb9c3a8368ccdcac14ff56b20706f534d7f81655715605 OP_CHECKSIGVERIFY 02646cce7f0159537815819139aad2e6d33f4398f8f502a0391491c7bf25bca4de OP_CHECKSIGVERIFY 03e852e9b853a26d7199a8772382bec56b9da7478b8037112c7d16d80d7c683568 OP_CHECKSIGVERIFY 0308d04709baf88bc6f5670d57c332ca872a8707207f3a5a59272940eb9ebc2936 OP_CHECKSIGVERIFY 0393cf1028583df1323f3b712edeb68078d412fabd1e5ccde839a500ddf506885a OP_CHECKSIGVERIFY 020edb22b296434cfe44b51f8dfc33579dd587e57a5f147470564ba9f817a586c8 OP_CHECKSIGVERIFY 0233de8260c26cba2851efc0ca2f695c37008929a2dc9244298c1990b36990083a OP_CHECKSIGVERIFY 02bcae3f6aecfd066ca029dfc833fc5f9f799760ab4baed6ef8c5ff94c002a7bc6 OP_CHECKSIGVERIFY 0285bb427b63bc8ed0890132d91a9283c7b82aa3f6d7fa7b3b27a74e7ffa41373c OP_CHECKSIGVERIFY 024e4c4f5ad562855256ba6380aba4bc93d7699939ace07c7c53fbecce044836c1 OP_CHECKSIGVERIFY 030895c63aebb5ce9e1ea2b3f30ebfd23f55fed7698757c1ec03154f1d581f0531 OP_CHECKSIGVERIFY 02070a7d5b57bf8c67ba34ed35108ba26579dbb84703c043886641e6482be0ae4a OP_CHECKSIGVERIFY 029c6ce3700ffe9b47b008c4cd849d4cd6ed1264eedebc0a4e1165dcbf5db503c6 OP_CHECKSIGVERIFY 02b1aa852b32f35e73a6aa070db64bc70329ec34f3ec13081b3859d45bb19e83b9 OP_CHECKSIGVERIFY 0375b9b9b287208cc57e49e15511f2c853bbb5562ed9fdd7b35e561d7e429018fc OP_CHECKSIGVERIFY 0355b3f1730e8eee06687bc128a86e3724dbe4da8a26fd3bfa54482ca25b247a99 OP_CHECKSIGVERIFY 03e9960ff1268abaff7190ba9f34f480b1279a6a52f96608313324c79214287bd1 OP_CHECKSIGVERIFY 036c8755595e5f3cfe45585237cbbec13737c9dab16ef90feca4b59cc20a3a5535 OP_CHECKSIGVERIFY 0228adcffece12cc99217f1d763aac469754fe3e49894a16a813443e29532e9651 OP_CHECKSIGVERIFY 03a0ea1dc9043e2f67450bb8ddccaa3af31964077a25b2baef62099fe0515fbd21 OP_CHECKSIGVERIFY 037de9ef6bee93474f6e02e377721543f8f4684e7074b45584fd751881aee96cf7 OP_CHECKSIGVERIFY 032874661d4c3802ad9913852a0271cb45cc90d981e662d831bd5290f2d6fe138d OP_CHECKSIGVERIFY 03e9242d1c7ed3ba76f8eba3e2fb4c5d33d667425222b95407de9209790e1b4bd3 OP_CHECKSIGVERIFY 02473773327552dc9f3652b513c68e133cc4d39202b348bbed359bc7b00f91bade OP_CHECKSIGVERIFY 03cebb99348541461de36106a68b0c7063bd4321021a21afbe90fa05d90a3444f0 OP_CHECKSIGVERIFY 03544a4accbdebb1bc0c62dfd3055dea9bd3c610c161a10afd53b4646d736a17db OP_CHECKSIGVERIFY 02baa91fc638c57fff9211334dc9c98b9937d75b7c534bdaeefcdeb56ca8f997e3 OP_CHECKSIGVERIFY 03bd8ea1948d869143f1fc0604ec8b54c95dd6cfc6cce11800bbe82b92ae9b5b74 OP_CHECKSIGVERIFY 0319ab36ae3727162c6699d6cfa76389a49d06a4bd4d4dad47606f8deec0376e75 OP_CHECKSIGVERIFY 0293003a1bca4ad83ac3bf5a2224ef782c8d7290f4f492e3588e644ffe02b3b35a OP_CHECKSIGVERIFY 0395248d8852d32e11f3288fa4f39a36fcc8225da9289fc3d0ad65f409821c4e17 OP_CHECKSIGVERIFY 02e3bee6809b08ab3b9fdbb4a7fcaa64cb602fdae173c6ce1c4042ca36f60e90fd OP_CHECKSIGVERIFY 034b74d1136c5422239d8c4a869e6cff7985204d9dedb2c3cca0b0b9afd589a35f OP_CHECKSIGVERIFY 0318e3f8941cf82c788d5dce82256bfe9522dd87f63a467e01c5004fec6667f0fe OP_CHECKSIGVERIFY 023fcda99ba2c461c65a3d95d03ab251d29ae9bd7b4d90a485aa6d075e35460e93 OP_CHECKSIGVERIFY 0329f7b047301f8f29725a9dd93e4742460e54b4c2ac9c08cfa9c4cd6a5e14a4ed OP_CHECKSIGVERIFY 0343a0c3f0fbbfd4ed0d1677c1a0cb3bb2a59dfcecdbc4af4386a6542cf349f437 OP_CHECKSIGVERIFY 02eae41bb436873f562629380ffdaf0fe5353cb5be07bd95343adf32ee984ef0ce OP_CHECKSIGVERIFY 02d71aa051ea95ba899d3e721f12a8ede4aebdd265a2ff31081128033bdd66befc OP_CHECKSIGVERIFY 029faa4411b3bc3a2778d6d3236396d2a81afefc792d07909e212de58e0765fa07 OP_CHECKSIGVERIFY 03eb3b7d5a44fcc754383ceca274a250b9a48a348ef166fad19981d5f37781d5dc OP_CHECKSIGVERIFY 03f3ccef99da37185f238f7643b7ff553e4f62fa376cf1852a49cd80cd856e444b OP_CHECKSIGVERIFY 03ba7b6f4ea978f9c5277c97d733ae0a2f55062555997971d6ed4b37d399b4f130 OP_CHECKSIGVERIFY 0217209004fa5e8b2277767caeac19a9c26c71655c917cf61aad6817f0717fcba0 OP_CHECKSIGVERIFY 0363f2e57f2ea1a49a39ed0542e1fdbc83e2d2317a2d29b058c7d82233f3c54e8a OP_CHECKSIGVERIFY 03dfd044cc08b325121055155e90f2955771f33f0e4d5462967dbeae431ac34e4b OP_CHECKSIGVERIFY 02aa29194b8666b1526b32e7b54cebe8305637837c7821399577ee95cf7bca6a36 OP_CHECKSIGVERIFY 02e11f095aa2730bfa6ab6a6d088d015e92204a70ad36c5fe6a6a97cf4be8fdc34 OP_CHECKSIGVERIFY 02d8a0b231ca61468d86abd8bf5169bd231243a4f75184f64a6946abb8eee17b9b OP_CHECKSIGVERIFY 034653d87fbdc8cff324a5f6e3220150749cff56f39f1bb378ae73301c15caca05 OP_CHECKSIGVERIFY 02b0e8830c020a01c31d8d211708ad1d04e7309bb584b8489d35fbcf12c09c84eb OP_CHECKSIGVERIFY 030b94559a4b54fc895f861bb2b5669736a709994ade45b08f9f6148ef25dc4eca OP_CHECKSIGVERIFY 02c1d4e3bd6651e273030a6d1f436a2455fa51b50390ba045c5e78bbef03eeb78f OP_CHECKSIGVERIFY 02b7d7f60abf61f50bcbaa0435f9a31a231b8f26def0f172578d0838091f7906eb OP_CHECKSIGVERIFY 02d410fbd6d899b21c3f6d380aeafea56a2e4e0e3b70f46734a716134fee793054 OP_CHECKSIGVERIFY 02bac3d71349f3dd83bc59c15c661d1520f43475f5fc0c38698b37de978419248b OP_CHECKSIGVERIFY 0277b4f24cc552577374909a853c1d570404e9072368d48310ed3ee597c9579e9d OP_CHECKSIGVERIFY 037ab3bd19731038c03bf4eec27e4e4788a8634b52c41e364dea45130b3ac8874b OP_CHECKSIGVERIFY 03102af5eb78a65b542b878e4e3eaa29f83f2efdae8838afe75cf2f85017b3f683 OP_CHECKSIGVERIFY 02452e8e88d61c0b206b9e4a319dc262662f4f98a5737e13b7abe2ceeae2bb8dcf OP_CHECKSIGVERIFY 0360d3d51bef5d6c5932e61b2ab44c7eea23a2d03cfed2422656ed1c873286a8e1 OP_CHECKSIGVERIFY 021eadd72a30b0ff9c2bc0f0b2815352fc789b85bf958ba36bde39092967968094 OP_CHECKSIGVERIFY 02631804ca57f694c27885d2d8c5e31e1f3b741a23d8c2e95a1ccd9ac8392c3c25 OP_CHECKSIGVERIFY 021ec0824b1594998ff91ddd2a7b53099ba60de84d447b13fddebf34533d111a3e OP_CHECKSIGVERIFY 039b386361ac56bd2b73186b66b77cb6de2fffbe05326dd2abf23d6b9d26b629ea OP_CHECKSIGVERIFY 0262efb44256f6ae1f1965aa24b57847e13f80a4283cb44a257572993845439a0e OP_CHECKSIGVERIFY 0283f818f966acc4894b54197eb7e94221851acfb6c986a7862ad86f961ab50a57 OP_CHECKSIGVERIFY 03a418b8eb7da20d694e257de6a9fc493676e1f98fef9f8165f69cd8d0f8099439 OP_CHECKSIGVERIFY 03697922030495b328ea3c101a1852ffd7818aa10fbedaf6c3a88b6fa381694d4b OP_CHECKSIGVERIFY 0266a13940d79ce8a272da6094df36875fd9c337c83d4ef491e0fa2a47ab3ffc57 OP_CHECKSIGVERIFY 03747ae0c8012281475a00085795b8bb0d595ebf2322f57ebd7c6b1edbbab56f92 OP_CHECKSIGVERIFY 02e357b1c82274aea4357e36278f912338e3adf1c17d8829edf394a1cc2bedc185 OP_CHECKSIGVERIFY 02dcc75706face7293b47b2e60af164971f9920118986ebf5ea147d077722e316f OP_CHECKSIGVERIFY 0347cdc7f774f1291bc45c7cb1d86eb7ea66a24622e387ccc7ac580b750f1ad9c3 OP_CHECKSIGVERIFY 02e650efb11ecd8ab320b3e5bec0d5852db1cddc1d67ede952f3c042f90dae1e43 OP_CHECKSIGVERIFY 039c24916f37a11398d4186a634414ea371003595e6444c92d01ace02bab5267ea OP_CHECKSIGVERIFY 038a5dbb9595503741fafe92f60187bfb2aad442dda5f9e6c17e5be6ec4b5521aa OP_CHECKSIGVERIFY 0291909a41d7114fc99e81224e37c030efc0c73b33e6b0eedcd8a53bfee276f108 OP_CHECKSIGVERIFY 02da51f2cc61ba9522869d2f9a34a21288cc8d017e4cf0060c85f2fff3ceacaf28 OP_CHECKSIGVERIFY 03237812d3a5ec0c49d8a60b15c2ac676cffc7eabf459dc9ef0ae215a1cbd26bb7 OP_CHECKSIGVERIFY 020e33b6b74b7f7190b0966b4f295199244ed33cea6e324c2f39a227486d87e059 OP_CHECKSIGVERIFY 02d2c96fd98d5da619ef2033892026b7f7595fd03667ab1f0b39a59d2e45a58c98 OP_CHECKSIGVERIFY 03df7ed4e582c8997b3a4c1138fac76144c02a052c7ac2c40e4e34d50e308efb42 OP_CHECKSIGVERIFY 028f343af70f46773a48bd8fcbeeb34a15f3cfee8427b62750006da2ae8a0745fc OP_CHECKSIGVERIFY 027e2f0c4f3d8d90ea0b426c82a6d41226e7b7c579724302034f844fde9a34bf96 OP_CHECKSIGVERIFY 03abdfa8a173a9be3a2669346430dcf5cbf5e2bb2321392bd661b9fa88be82677b OP_CHECKSIG"
+ }
+ }
+ },
{
"exception": "Non push-only scriptSig",
"arguments": {
diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json
index 0e51d57cf..7e971ac54 100644
--- a/test/fixtures/psbt.json
+++ b/test/fixtures/psbt.json
@@ -116,6 +116,10 @@
{
"description": "PSBT with unknown types in the inputs.",
"psbt": "cHNidP8BAD8CAAAAAf//////////////////////////////////////////AAAAAAD/////AQAAAAAAAAAAA2oBAAAAAAAACg8BAgMEBQYHCAkPAQIDBAUGBwgJCgsMDQ4PAAA="
+ },
+ {
+ "description": "PSBT with one P2TR input and one P2TR output.",
+ "psbt": "cHNidP8BAF4CAAAAAWbQAKi9hNXynJhqPu8bqkvp0kHihVShkWGh3yLy15+LAAAAAAD/////Aej9AAAAAAAAIlEgRvZJfLLxnVDD6emCqVDcyGIUsB/M5DekIGHHvbEjDTMAAAAAAAEBK7gFAQAAAAAAIlEglCHnNLD50sRn6n3Rl8Yay0RnzcvJ9MsMVx+LY6XEDK4AAA=="
}
],
"failSignChecks": [
@@ -192,7 +196,7 @@
{
"witnessUtxo": {
"script": "Buffer.from('a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e887', 'hex')",
- "value": 200000000
+ "value": "200000000"
},
"redeemScript": "Buffer.from('00208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903', 'hex')",
"witnessScript": "Buffer.from('522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae', 'hex')",
@@ -243,6 +247,36 @@
}
],
"result": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAABAwQBAAAAAQRHUiEClYO/Oa4KYJdHrRma3dY0+mEIVZ1sXNObTCGD8auW4H8hAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXUq4iBgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfxDZDGpPAAAAgAAAAIAAAACAIgYC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtcQ2QxqTwAAAIAAAACAAQAAgAABASAAwusLAAAAABepFLf1+vQOPUClpFmx2zU18rcvqSHohwEDBAEAAAABBCIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAQVHUiEDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwhAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zUq4iBgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8OcxDZDGpPAAAAgAAAAIADAACAIgYDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwQ2QxqTwAAAIAAAACAAgAAgAAiAgOppMN/WZbTqiXbrGtXCvBlA5RJKUJGCzVHU+2e7KWHcRDZDGpPAAAAgAAAAIAEAACAACICAn9jmXV9Lv9VoTatAsaEsYOLZVbl8bazQoKpS2tQBRCWENkMak8AAACAAAAAgAUAAIAA"
+ },
+ {
+ "description": "Update taproot output",
+ "isTaproot": true,
+ "psbt": "cHNidP8BAF4CAAAAAbc48X2AGPRzFoMvXHqldFViBB89ZrPfTH6yxr42pJsWAAAAAAAKAAAAAZBBBgAAAAAAIlEgwjn5jxVDmCN6B+wTPt4zCjlF+5KOEDapZ4S9Y3HZxdwAAAAAAAEBK6BoBgAAAAAAIlEgOJumKbXWO2VGchS5k1l1NzOsAyg8aBVQbDNJ88BPtH5iFcF6ESg3Xx4VxbYhRfEEyJc6dumg3zlxfNO8piELt1JMShpSnJ+zzX53bWG2IltsYQ6JBvuPqmxZrFw+lbX4LSnWGlKcn7PNfndtYbYiW2xhDokG+4+qbFmsXD6VtfgtKdYmWrJ1IAOmFFKDvSwulomGXZP6LYOYOGBHzSRnrh2w9cb0vzIGrMAAAA==",
+ "outputData": [
+ {
+ "tapInternalKey": "Buffer.from('b3310106b13af5def8fc341fe7120e844ab73d4437649aefcadd306d974c9958', 'hex')",
+ "tapTree": {
+ "leaves": [
+ {
+ "depth": 1,
+ "leafVersion": 192,
+ "script": "Buffer.from('2050929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0ac', 'hex')"
+ },
+ {
+ "depth": 2,
+ "leafVersion": 192,
+ "script": "Buffer.from('2050929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0ac', 'hex')"
+ },
+ {
+ "depth": 2,
+ "leafVersion": 192,
+ "script": "Buffer.from('5ab2752003a6145283bd2c2e9689865d93fa2d8398386047cd2467ae1db0f5c6f4bf3206ac', 'hex')"
+ }
+ ]
+ }
+ }
+ ],
+ "result": "cHNidP8BAF4CAAAAAbc48X2AGPRzFoMvXHqldFViBB89ZrPfTH6yxr42pJsWAAAAAAAKAAAAAZBBBgAAAAAAIlEgwjn5jxVDmCN6B+wTPt4zCjlF+5KOEDapZ4S9Y3HZxdwAAAAAAAEBK6BoBgAAAAAAIlEgOJumKbXWO2VGchS5k1l1NzOsAyg8aBVQbDNJ88BPtH5iFcF6ESg3Xx4VxbYhRfEEyJc6dumg3zlxfNO8piELt1JMShpSnJ+zzX53bWG2IltsYQ6JBvuPqmxZrFw+lbX4LSnWGlKcn7PNfndtYbYiW2xhDokG+4+qbFmsXD6VtfgtKdYmWrJ1IAOmFFKDvSwulomGXZP6LYOYOGBHzSRnrh2w9cb0vzIGrMAAAQUgszEBBrE69d74/DQf5xIOhEq3PUQ3ZJrvyt0wbZdMmVgBBnIBwCIgUJKbdMGgSVS3i0tgNel6XgeKWg8o7JbVR7/ums6AOsCsAsAiIFCSm3TBoElUt4tLYDXpel4HiloPKOyW1Ue/7prOgDrArALAJVqydSADphRSg70sLpaJhl2T+i2DmDhgR80kZ64dsPXG9L8yBqwA"
}
],
"signer": [
@@ -273,6 +307,71 @@
}
],
"result": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAAiAgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU210gwRQIhAPYQOLMI3B2oZaNIUnRvAVdyk0IIxtJEVDk82ZvfIhd3AiAFbmdaZ1ptCgK4WxTl4pB02KJam1dgvqKBb2YZEKAG6gEBAwQBAAAAAQRHUiEClYO/Oa4KYJdHrRma3dY0+mEIVZ1sXNObTCGD8auW4H8hAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXUq4iBgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfxDZDGpPAAAAgAAAAIAAAACAIgYC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtcQ2QxqTwAAAIAAAACAAQAAgAABASAAwusLAAAAABepFLf1+vQOPUClpFmx2zU18rcvqSHohyICAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zRzBEAiBl9FulmYtZon/+GnvtAWrx8fkNVLOqj3RQql9WolEDvQIgf3JHA60e25ZoCyhLVtT/y4j3+3Weq74IqjDym4UTg9IBAQMEAQAAAAEEIgAgjCNTFzdDtZXftKB7crqOQuN5fadOh/59nXSX47ICiQMBBUdSIQMIncEMesbbVPkTKa9hczPbOIzq0MIx9yM3nRuZAwsC3CECOt2QTz1tz1nduQaw3uI1Kbf/ue1Q5ehhUZJoYCIfDnNSriIGAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zENkMak8AAACAAAAAgAMAAIAiBgMIncEMesbbVPkTKa9hczPbOIzq0MIx9yM3nRuZAwsC3BDZDGpPAAAAgAAAAIACAACAACICA6mkw39ZltOqJdusa1cK8GUDlEkpQkYLNUdT7Z7spYdxENkMak8AAACAAAAAgAQAAIAAIgICf2OZdX0u/1WhNq0CxoSxg4tlVuXxtrNCgqlLa1AFEJYQ2QxqTwAAAIAAAACABQAAgAA="
+ },
+ {
+ "description": "Sign PSBT with 3 inputs [P2PKH, P2TR (key-path), P2WPKH] and two outputs [P2TR, P2WPKH]",
+ "psbt": "cHNidP8BAM8CAAAAAwPzd9k+uLSN1rgF01xY1TIA/8N+YytNZ4VP9gKFP4MyAAAAAAD/////ZtAAqL2E1fKcmGo+7xuqS+nSQeKFVKGRYaHfIvLXn4sAAAAAAP////9+h+SlCwIx1MUDT7Bek0NrWXS7xnSPi5LbYbDc9sxYIgAAAAAA/////wIgKRsAAAAAACJRIEb2SXyy8Z1Qw+npgqlQ3MhiFLAfzOQ3pCBhx72xIw0zuAUBAAAAAAAWABTJijE0v48z5ZmmfEAADXdCBcG0FAAAAAAAAQDiAgAAAAABAUfY2D1t0dyMeEH39C1yOdIxigpqm7XJNqHVT3Lc+FkiAAAAAAD+////AhIsGwAAAAAAGXapFJ5+8XZ3ZP80oFldvEwrcNsBftBmiKyYdK6xAAAAABepFLDBn59UffGbX7u/olyFDG0eG1UJhwJHMEQCIDAd3s05C61flXVFqOtov0NoHRGr8KFcOpH6R/81F46EAiBt+j9hHyvT2hYEyf8fdYsM9IgbnybtPV+kRTHDa6Rj0AEhAmmZfwmoHsmCkEOn9AfRTh+863mURelmE8hSqL4MG1EydJwgAAABASu4BQEAAAAAACJRIE/vXFFjvqaak+dKWWcrvrCBg3B3y5TPpuSBpc8A2KsYARcglCHnNLD50sRn6n3Rl8Yay0RnzcvJ9MsMVx+LY6XEDK4AAQEfECcAAAAAAAAWABRPoqyhKLb53uUwnE9wBR5Jxl/XMgAAAA==",
+ "isTaproot": true,
+ "keys": [
+ {
+ "inputToSign": 0,
+ "WIF": "cRyKzLXVgTReWe7wgfEiXktTa9tf4e5DK1STha274d7BBbnucTaR"
+ },
+ {
+ "inputToSign": 1,
+ "WIF": "cR62L1G154fjHFrBCJMxJxbk1rcxhT2xcTh7WstvFdFDsZ9uFiVj"
+ },
+ {
+ "inputToSign": 2,
+ "WIF": "cPPRdCmAMZMjPdHfRmTCmzYVruZHJ8GbM1FqN2W6DnmEPWDg29aL"
+ }
+ ],
+ "result": "cHNidP8BAM8CAAAAAwPzd9k+uLSN1rgF01xY1TIA/8N+YytNZ4VP9gKFP4MyAAAAAAD/////ZtAAqL2E1fKcmGo+7xuqS+nSQeKFVKGRYaHfIvLXn4sAAAAAAP////9+h+SlCwIx1MUDT7Bek0NrWXS7xnSPi5LbYbDc9sxYIgAAAAAA/////wIgKRsAAAAAACJRIEb2SXyy8Z1Qw+npgqlQ3MhiFLAfzOQ3pCBhx72xIw0zuAUBAAAAAAAWABTJijE0v48z5ZmmfEAADXdCBcG0FAAAAAAAAQDiAgAAAAABAUfY2D1t0dyMeEH39C1yOdIxigpqm7XJNqHVT3Lc+FkiAAAAAAD+////AhIsGwAAAAAAGXapFJ5+8XZ3ZP80oFldvEwrcNsBftBmiKyYdK6xAAAAABepFLDBn59UffGbX7u/olyFDG0eG1UJhwJHMEQCIDAd3s05C61flXVFqOtov0NoHRGr8KFcOpH6R/81F46EAiBt+j9hHyvT2hYEyf8fdYsM9IgbnybtPV+kRTHDa6Rj0AEhAmmZfwmoHsmCkEOn9AfRTh+863mURelmE8hSqL4MG1EydJwgACICAi5ovBH1xLoGxPqtFh48wUEpnM+St1SbPzRwO7kBNKOQRzBEAiBpWClBybtHveXkhAgTiE8QSczMJs8MGuH4LOSNRA6s/AIgWlbB3xJOtJIsszj1qZ/whA5jK9wnTzeZzDlVs/ivq2cBAAEBK7gFAQAAAAAAIlEgT+9cUWO+ppqT50pZZyu+sIGDcHfLlM+m5IGlzwDYqxgBE0BhU0RJZPtgS8Nfo3rQIalmkhtIYCuTSrpp9lGYuxRkdD8ulwzPJKz+hkQU5C1u068lN3uVHKs1XOTm6t0Dl/18ARcglCHnNLD50sRn6n3Rl8Yay0RnzcvJ9MsMVx+LY6XEDK4AAQEfECcAAAAAAAAWABRPoqyhKLb53uUwnE9wBR5Jxl/XMiICA6VOpEBbPJM/xYsqO2euttYFpgec9vcxggyTyoklK660SDBFAiEAoCIktghL55iuMAmkzwYJzb+h+qmNewZXxAx/06ObxIQCIELCsBz/wd2wPlnJb27OluxMkTPnCyHA2C+SxHiX/FvPAQAAAA=="
+ },
+ {
+ "description": "Sign PSBT with 1 input [P2TR] (script-path, 3-of-3) and one output [P2TR]",
+ "psbt": "cHNidP8BAF4CAAAAAQU2GLj/HTOEN804Hc6JRNTLM7EmDlIBdjG2G1aUw266AAAAAAD/////AYAaBgAAAAAAIlEg35sLGepBXUbR93XfxDJcYBCHqNVjw/jogOgPVdic1UgAAAAAAAEBK6BoBgAAAAAAIlEgVZx4+tHeORcb0jDJnOytrOnNOGL1uS0MdMUg1GQeS15iFcFmuQP4s1ds7KJtMOh4fTw1QCgxkWUA3FUAuUzKHzjDvhpSnJ+zzX53bWG2IltsYQ6JBvuPqmxZrFw+lbX4LSnWGlKcn7PNfndtYbYiW2xhDokG+4+qbFmsXD6VtfgtKdZpII+JGq8uLXsUYXjFSofirt3Z0daGaSJXko5nzwgXT/N1rCA5X4Ep3WO0pcLxISTqoFt6ftMPcOUfuTMF3uzFQuf567ogqKs3vBYJ2DTDkTs1ONrRyE1/m2qDX/wXV3eRWjeuNXK6U5zAAAA=",
+ "isTaproot": true,
+ "keys": [
+ {
+ "inputToSign": 0,
+ "WIF": "cRXSy63fXDve59e8cvqozVFfqXJB6YL6cPzoRewmEsux81SgPrfj"
+ },
+ {
+ "inputToSign": 0,
+ "WIF": "cQQXUJocNBS6oZCCtyhCsdN5ammK6WoJWpx44ANKxZSN2A3WDDEN"
+ },
+ {
+ "inputToSign": 0,
+ "WIF": "cTrPNrN2EQo4ppBHcFNxyLBFq2WLjZoNKY5nQbPwAGdhQqqsRKSu"
+ }
+ ],
+ "result": "cHNidP8BAF4CAAAAAQU2GLj/HTOEN804Hc6JRNTLM7EmDlIBdjG2G1aUw266AAAAAAD/////AYAaBgAAAAAAIlEg35sLGepBXUbR93XfxDJcYBCHqNVjw/jogOgPVdic1UgAAAAAAAEBK6BoBgAAAAAAIlEgVZx4+tHeORcb0jDJnOytrOnNOGL1uS0MdMUg1GQeS15BFDlfgSndY7SlwvEhJOqgW3p+0w9w5R+5MwXe7MVC5/nrrqI0FdZdKILLLZgRVK8L9Bn2ijU6IcoqqyImKIWt3MtA0Qs3hMTM9htXOJ9ma+GJHzDabXGSm0YQjLuov1fgN+2897h6AdMVJHFbOfPN0hKeh2+jlH6oOD4jMxm4bfp1cUEUj4kary4texRheMVKh+Ku3dnR1oZpIleSjmfPCBdP83WuojQV1l0ogsstmBFUrwv0GfaKNTohyiqrIiYoha3cy0B0zkdTBdMYKfT/G05vAI/nHDvsu+7TxwXN1WNA4hDaR1wIcNi/qNGN3EMBrPoM0aU+U9s1VPgHgGJH/VFIBz88QRSoqze8FgnYNMOROzU42tHITX+baoNf/BdXd5FaN641cq6iNBXWXSiCyy2YEVSvC/QZ9oo1OiHKKqsiJiiFrdzLQMhiSbnEKdnqXOdAYgqjmFY1Y9F5+y/cHmjbd9NvFOxHaNOJakOajrCWkqa19CwWKZJ3lgxKqSFLT1zbt8sbrRFiFcFmuQP4s1ds7KJtMOh4fTw1QCgxkWUA3FUAuUzKHzjDvhpSnJ+zzX53bWG2IltsYQ6JBvuPqmxZrFw+lbX4LSnWGlKcn7PNfndtYbYiW2xhDokG+4+qbFmsXD6VtfgtKdZpII+JGq8uLXsUYXjFSofirt3Z0daGaSJXko5nzwgXT/N1rCA5X4Ep3WO0pcLxISTqoFt6ftMPcOUfuTMF3uzFQuf567ogqKs3vBYJ2DTDkTs1ONrRyE1/m2qDX/wXV3eRWjeuNXK6U5zAAAA="
+ },
+ {
+ "description": "Sign PSBT with 1 input [P2TR]. Signer pubkey found in two tapleaf scripts (both get signed)",
+ "psbt": "cHNidP8BAF4CAAAAATNOP+fCPDCsZrgGIptDVbwY/yrkM2xaFfLe05BSLulZAAAAAAD/////AZBBBgAAAAAAIlEgo6glLro4LvJCjIXCj+2xYC8NQ5BB/tvaXLukmWNHDaQAAAAAAAEBK6BoBgAAAAAAIlEgo6glLro4LvJCjIXCj+2xYC8NQ5BB/tvaXLukmWNHDaRiFcDXgjtX0c88beKd6Y8zRYbANZXEc8dDhlup8v3SCiX0bBpSnJ+zzX53bWG2IltsYQ6JBvuPqmxZrFw+lbX4LSnWWeEQK6JF8Ev4E8Yg25gvDzbizKAO6m6JD082gFjsWNVpIP1IuUG/pcYMyG7KOHraqMXMKKOJb5eIpnjGt7LrvcsWrCDxKlbBaB54iRPj/r6t5N9DtBFItV8uoHfEwGboXpVsHLogt01J+NwSsj7PM7m6QbLDiXLdoYzn9pHNA4kAGF1pL3+6U5zAQhXA14I7V9HPPG3inemPM0WGwDWVxHPHQ4ZbqfL90gol9Gx4Lk64Mk5QMzz2Xo97sRZFcGH0TduAOb6H0hHOB/0CrCMg/Ui5Qb+lxgzIbso4etqoxcwoo4lvl4imeMa3suu9yxaswAAA",
+ "isTaproot": true,
+ "keys": [
+ {
+ "inputToSign": 0,
+ "WIF": "cSu5bjn9TsAEeqZxEFKy5of3FKfHha6FT56KvfAGmu6rMmPNJTrV"
+ }
+ ],
+ "result": "cHNidP8BAF4CAAAAATNOP+fCPDCsZrgGIptDVbwY/yrkM2xaFfLe05BSLulZAAAAAAD/////AZBBBgAAAAAAIlEgo6glLro4LvJCjIXCj+2xYC8NQ5BB/tvaXLukmWNHDaQAAAAAAAEBK6BoBgAAAAAAIlEgo6glLro4LvJCjIXCj+2xYC8NQ5BB/tvaXLukmWNHDaRBFP1IuUG/pcYMyG7KOHraqMXMKKOJb5eIpnjGt7LrvcsWIjJncH2zqZGpUloPoJZipi/NkKmjWthVrvnedXPOOOdAWh/SY0SIN0yP6TggtuXbx2KEGSKx4ywGaj9Irldz2JzKphk7uFUo49THWtJ0OinN6Wk0pA9CTynHjFEkOivhSEEU/Ui5Qb+lxgzIbso4etqoxcwoo4lvl4imeMa3suu9yxZZ4RArokXwS/gTxiDbmC8PNuLMoA7qbokPTzaAWOxY1UBRckOaOAdI93odstKtTZKwDVMinPkXg85GnzL3BQWfuttr86geYaEJmnTWeyzKhGTu5MYPGPX0BuE6ThCBR7pQYhXA14I7V9HPPG3inemPM0WGwDWVxHPHQ4ZbqfL90gol9GwaUpyfs81+d21htiJbbGEOiQb7j6psWaxcPpW1+C0p1lnhECuiRfBL+BPGINuYLw824sygDupuiQ9PNoBY7FjVaSD9SLlBv6XGDMhuyjh62qjFzCijiW+XiKZ4xrey673LFqwg8SpWwWgeeIkT4/6+reTfQ7QRSLVfLqB3xMBm6F6VbBy6ILdNSfjcErI+zzO5ukGyw4ly3aGM5/aRzQOJABhdaS9/ulOcwEIVwNeCO1fRzzxt4p3pjzNFhsA1lcRzx0OGW6ny/dIKJfRseC5OuDJOUDM89l6Pe7EWRXBh9E3bgDm+h9IRzgf9AqwjIP1IuUG/pcYMyG7KOHraqMXMKKOJb5eIpnjGt7LrvcsWrMAAAA=="
+ },
+ {
+ "description": "Sign PSBT with 1 input [P2TR]. Signer pubkey found in two tapleaf scripts (sign only the matching tapleaf hash)",
+ "psbt": "cHNidP8BAF4CAAAAAdXMkUOLeYvgm981k3T7Pmdf5Dr31jOxvpFHiXiU9D2gAAAAAAD/////AZBBBgAAAAAAIlEgD31MIAvwCaanOsAzyBoQ4o/WozgFiqVQTEqGJE18XqwAAAAAAAEBK6BoBgAAAAAAIlEgD31MIAvwCaanOsAzyBoQ4o/WozgFiqVQTEqGJE18XqxiFcAI1JOUM3VATBJC28HI1B5zKQNd5N4/c4g7M1CYhZWnWhpSnJ+zzX53bWG2IltsYQ6JBvuPqmxZrFw+lbX4LSnWk4qTbS/aPOMicn4cednnrKiVhdggWbZxsQhaXXg5/EtpICo/Wsfk0hSOCy21lUyn+vXnc1SG1lNVWHlnXCh3xegqrCCcumXmHlTmveMSBLOPSfb6H3laX97tZtF4gqmxzV+gbLogXBeUvuJ0b/mMOa3539xiz0/Nf9RQCWGZXXoCNcFUYde6U5zAQhXACNSTlDN1QEwSQtvByNQecykDXeTeP3OIOzNQmIWVp1pfFb/yNVlibfG8oN35ON4kJ+kBZQ4LphUz16+7jituPiMgKj9ax+TSFI4LLbWVTKf69edzVIbWU1VYeWdcKHfF6CqswAAA",
+ "isTaproot": true,
+ "keys": [
+ {
+ "inputToSign": 0,
+ "tapLeafHashToSign": "f2d9fd9a2f80e0e7abeac881398fc37198f46e5c802ec00c95152aa6f703e71e",
+ "WIF": "cP76Rzf6bVcmFbnJ3DigWvyNvki2bZeXxoq3B5pcZ8zVRnT4fKdx"
+ }
+ ],
+ "result": "cHNidP8BAF4CAAAAAdXMkUOLeYvgm981k3T7Pmdf5Dr31jOxvpFHiXiU9D2gAAAAAAD/////AZBBBgAAAAAAIlEgD31MIAvwCaanOsAzyBoQ4o/WozgFiqVQTEqGJE18XqwAAAAAAAEBK6BoBgAAAAAAIlEgD31MIAvwCaanOsAzyBoQ4o/WozgFiqVQTEqGJE18XqxBFCo/Wsfk0hSOCy21lUyn+vXnc1SG1lNVWHlnXCh3xegq8tn9mi+A4Oer6siBOY/DcZj0blyALsAMlRUqpvcD5x5AobIOM9VQGlOm3DOObInhbKG6m528jHocxgMLE+6KDdfcB/YPZ6qkdSt8Zhl3Jg9vsvNsGYqZJiRy7as7IGD03WIVwAjUk5QzdUBMEkLbwcjUHnMpA13k3j9ziDszUJiFladaGlKcn7PNfndtYbYiW2xhDokG+4+qbFmsXD6VtfgtKdaTipNtL9o84yJyfhx52eesqJWF2CBZtnGxCFpdeDn8S2kgKj9ax+TSFI4LLbWVTKf69edzVIbWU1VYeWdcKHfF6CqsIJy6ZeYeVOa94xIEs49J9vofeVpf3u1m0XiCqbHNX6BsuiBcF5S+4nRv+Yw5rfnf3GLPT81/1FAJYZldegI1wVRh17pTnMBCFcAI1JOUM3VATBJC28HI1B5zKQNd5N4/c4g7M1CYhZWnWl8Vv/I1WWJt8byg3fk43iQn6QFlDgumFTPXr7uOK24+IyAqP1rH5NIUjgsttZVMp/r153NUhtZTVVh5Z1wod8XoKqzAAAA="
}
],
"combiner": [
@@ -295,6 +394,42 @@
{
"psbt": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAAiAgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgf0cwRAIgdAGK1BgAl7hzMjwAFXILNoTMgSOJEEjn282bVa1nnJkCIHPTabdA4+tT3O+jOCPIBwUUylWn3ZVE8VfBZ5EyYRGMASICAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXSDBFAiEA9hA4swjcHahlo0hSdG8BV3KTQgjG0kRUOTzZm98iF3cCIAVuZ1pnWm0KArhbFOXikHTYolqbV2C+ooFvZhkQoAbqAQEDBAEAAAABBEdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSriIGApWDvzmuCmCXR60Zmt3WNPphCFWdbFzTm0whg/GrluB/ENkMak8AAACAAAAAgAAAAIAiBgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU21xDZDGpPAAAAgAAAAIABAACAAAEBIADC6wsAAAAAF6kUt/X69A49QKWkWbHbNTXyty+pIeiHIgIDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtxHMEQCIGLrelVhB6fHP0WsSrWh3d9vcHX7EnWWmn84Pv/3hLyyAiAMBdu3Rw2/LwhVfdNWxzJcHtMJE+mWzThAlF2xIijaXwEiAgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc0cwRAIgZfRbpZmLWaJ//hp77QFq8fH5DVSzqo90UKpfVqJRA70CIH9yRwOtHtuWaAsoS1bU/8uI9/t1nqu+CKow8puFE4PSAQEDBAEAAAABBCIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAQVHUiEDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwhAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zUq4iBgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8OcxDZDGpPAAAAgAAAAIADAACAIgYDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwQ2QxqTwAAAIAAAACAAgAAgAAiAgOppMN/WZbTqiXbrGtXCvBlA5RJKUJGCzVHU+2e7KWHcRDZDGpPAAAAgAAAAIAEAACAACICAn9jmXV9Lv9VoTatAsaEsYOLZVbl8bazQoKpS2tQBRCWENkMak8AAACAAAAAgAUAAIAA",
"result": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAABB9oARzBEAiB0AYrUGACXuHMyPAAVcgs2hMyBI4kQSOfbzZtVrWecmQIgc9Npt0Dj61Pc76M4I8gHBRTKVafdlUTxV8FnkTJhEYwBSDBFAiEA9hA4swjcHahlo0hSdG8BV3KTQgjG0kRUOTzZm98iF3cCIAVuZ1pnWm0KArhbFOXikHTYolqbV2C+ooFvZhkQoAbqAUdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSrgABASAAwusLAAAAABepFLf1+vQOPUClpFmx2zU18rcvqSHohwEHIyIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAQjaBABHMEQCIGLrelVhB6fHP0WsSrWh3d9vcHX7EnWWmn84Pv/3hLyyAiAMBdu3Rw2/LwhVfdNWxzJcHtMJE+mWzThAlF2xIijaXwFHMEQCIGX0W6WZi1mif/4ae+0BavHx+Q1Us6qPdFCqX1aiUQO9AiB/ckcDrR7blmgLKEtW1P/LiPf7dZ6rvgiqMPKbhROD0gFHUiEDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwhAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zUq4AIgIDqaTDf1mW06ol26xrVwrwZQOUSSlCRgs1R1Ptnuylh3EQ2QxqTwAAAIAAAACABAAAgAAiAgJ/Y5l1fS7/VaE2rQLGhLGDi2VW5fG2s0KCqUtrUAUQlhDZDGpPAAAAgAAAAIAFAACAAA=="
+ },
+ {
+ "description": "Finalze taproot key-path",
+ "psbt": "cHNidP8BAM8CAAAAAwPzd9k+uLSN1rgF01xY1TIA/8N+YytNZ4VP9gKFP4MyAAAAAAD/////ZtAAqL2E1fKcmGo+7xuqS+nSQeKFVKGRYaHfIvLXn4sAAAAAAP////9+h+SlCwIx1MUDT7Bek0NrWXS7xnSPi5LbYbDc9sxYIgAAAAAA/////wIgKRsAAAAAACJRIEb2SXyy8Z1Qw+npgqlQ3MhiFLAfzOQ3pCBhx72xIw0zuAUBAAAAAAAWABTJijE0v48z5ZmmfEAADXdCBcG0FAAAAAAAAQDiAgAAAAABAUfY2D1t0dyMeEH39C1yOdIxigpqm7XJNqHVT3Lc+FkiAAAAAAD+////AhIsGwAAAAAAGXapFJ5+8XZ3ZP80oFldvEwrcNsBftBmiKyYdK6xAAAAABepFLDBn59UffGbX7u/olyFDG0eG1UJhwJHMEQCIDAd3s05C61flXVFqOtov0NoHRGr8KFcOpH6R/81F46EAiBt+j9hHyvT2hYEyf8fdYsM9IgbnybtPV+kRTHDa6Rj0AEhAmmZfwmoHsmCkEOn9AfRTh+863mURelmE8hSqL4MG1EydJwgACICAi5ovBH1xLoGxPqtFh48wUEpnM+St1SbPzRwO7kBNKOQRzBEAiBpWClBybtHveXkhAgTiE8QSczMJs8MGuH4LOSNRA6s/AIgWlbB3xJOtJIsszj1qZ/whA5jK9wnTzeZzDlVs/ivq2cBAAEBK7gFAQAAAAAAIlEglCHnNLD50sRn6n3Rl8Yay0RnzcvJ9MsMVx+LY6XEDK4BE0Cd7/ny+QreV7urBWKNroQWCvnZczwkU0kLZiKsJQjtftKHWXMknftjt1d4K6aPYH7cBXzhlrUF+2GovjYLccZeARcglCHnNLD50sRn6n3Rl8Yay0RnzcvJ9MsMVx+LY6XEDK4AAQEfECcAAAAAAAAWABRPoqyhKLb53uUwnE9wBR5Jxl/XMiICA6VOpEBbPJM/xYsqO2euttYFpgec9vcxggyTyoklK660SDBFAiEAoCIktghL55iuMAmkzwYJzb+h+qmNewZXxAx/06ObxIQCIELCsBz/wd2wPlnJb27OluxMkTPnCyHA2C+SxHiX/FvPAQAAAA==",
+ "result": "cHNidP8BAM8CAAAAAwPzd9k+uLSN1rgF01xY1TIA/8N+YytNZ4VP9gKFP4MyAAAAAAD/////ZtAAqL2E1fKcmGo+7xuqS+nSQeKFVKGRYaHfIvLXn4sAAAAAAP////9+h+SlCwIx1MUDT7Bek0NrWXS7xnSPi5LbYbDc9sxYIgAAAAAA/////wIgKRsAAAAAACJRIEb2SXyy8Z1Qw+npgqlQ3MhiFLAfzOQ3pCBhx72xIw0zuAUBAAAAAAAWABTJijE0v48z5ZmmfEAADXdCBcG0FAAAAAAAAQDiAgAAAAABAUfY2D1t0dyMeEH39C1yOdIxigpqm7XJNqHVT3Lc+FkiAAAAAAD+////AhIsGwAAAAAAGXapFJ5+8XZ3ZP80oFldvEwrcNsBftBmiKyYdK6xAAAAABepFLDBn59UffGbX7u/olyFDG0eG1UJhwJHMEQCIDAd3s05C61flXVFqOtov0NoHRGr8KFcOpH6R/81F46EAiBt+j9hHyvT2hYEyf8fdYsM9IgbnybtPV+kRTHDa6Rj0AEhAmmZfwmoHsmCkEOn9AfRTh+863mURelmE8hSqL4MG1EydJwgAAEHakcwRAIgaVgpQcm7R73l5IQIE4hPEEnMzCbPDBrh+CzkjUQOrPwCIFpWwd8STrSSLLM49amf8IQOYyvcJ083mcw5VbP4r6tnASECLmi8EfXEugbE+q0WHjzBQSmcz5K3VJs/NHA7uQE0o5AAAQEruAUBAAAAAAAiUSCUIec0sPnSxGfqfdGXxhrLRGfNy8n0ywxXH4tjpcQMrgEIQgFAne/58vkK3le7qwVija6EFgr52XM8JFNJC2YirCUI7X7Sh1lzJJ37Y7dXeCumj2B+3AV84Za1BfthqL42C3HGXgABAR8QJwAAAAAAABYAFE+irKEotvne5TCcT3AFHknGX9cyAQhsAkgwRQIhAKAiJLYIS+eYrjAJpM8GCc2/ofqpjXsGV8QMf9Ojm8SEAiBCwrAc/8HdsD5ZyW9uzpbsTJEz5wshwNgvksR4l/xbzwEhA6VOpEBbPJM/xYsqO2euttYFpgec9vcxggyTyoklK660AAAA",
+ "isTaproot": true
+ },
+ {
+ "description": "Finalze taproot script-path",
+ "psbt": "cHNidP8BAF4CAAAAAQU2GLj/HTOEN804Hc6JRNTLM7EmDlIBdjG2G1aUw266AAAAAAD/////AYAaBgAAAAAAIlEg35sLGepBXUbR93XfxDJcYBCHqNVjw/jogOgPVdic1UgAAAAAAAEBK6BoBgAAAAAAIlEgVZx4+tHeORcb0jDJnOytrOnNOGL1uS0MdMUg1GQeS15BFDlfgSndY7SlwvEhJOqgW3p+0w9w5R+5MwXe7MVC5/nrrqI0FdZdKILLLZgRVK8L9Bn2ijU6IcoqqyImKIWt3MtAA3alBoU7IBCkBk9OHD1wE8fJI4y+lbnTRj48e8AAwRM77q3Rml679qCzGvEAKAs99UNMaXHQIhgGfRP11AMlJkEUj4kary4texRheMVKh+Ku3dnR1oZpIleSjmfPCBdP83WuojQV1l0ogsstmBFUrwv0GfaKNTohyiqrIiYoha3cy0DYJZ6Lv7FZPIBRZFfVgF5v3gcRiQnT8aM82Q5IPkwkzZrGo4ThZblvunG/+hu8ZPuJrUU+uXb+s9rcwSH+BihIQRSoqze8FgnYNMOROzU42tHITX+baoNf/BdXd5FaN641cq6iNBXWXSiCyy2YEVSvC/QZ9oo1OiHKKqsiJiiFrdzLQEfQ5UkAg4lTbhJxjMzB7hu6ad1fywYxHCXjFXHHrm5PJTOFJLg2oTnwuQToz/Z2AW/UET7Op+WSoHZvW4tzzLhiFcFmuQP4s1ds7KJtMOh4fTw1QCgxkWUA3FUAuUzKHzjDvhpSnJ+zzX53bWG2IltsYQ6JBvuPqmxZrFw+lbX4LSnWGlKcn7PNfndtYbYiW2xhDokG+4+qbFmsXD6VtfgtKdZpII+JGq8uLXsUYXjFSofirt3Z0daGaSJXko5nzwgXT/N1rCA5X4Ep3WO0pcLxISTqoFt6ftMPcOUfuTMF3uzFQuf567ogqKs3vBYJ2DTDkTs1ONrRyE1/m2qDX/wXV3eRWjeuNXK6U5zAAAA=",
+ "result": "cHNidP8BAF4CAAAAAQU2GLj/HTOEN804Hc6JRNTLM7EmDlIBdjG2G1aUw266AAAAAAD/////AYAaBgAAAAAAIlEg35sLGepBXUbR93XfxDJcYBCHqNVjw/jogOgPVdic1UgAAAAAAAEBK6BoBgAAAAAAIlEgVZx4+tHeORcb0jDJnOytrOnNOGL1uS0MdMUg1GQeS14BCP2PAQVAR9DlSQCDiVNuEnGMzMHuG7pp3V/LBjEcJeMVcceubk8lM4UkuDahOfC5BOjP9nYBb9QRPs6n5ZKgdm9bi3PMuEADdqUGhTsgEKQGT04cPXATx8kjjL6VudNGPjx7wADBEzvurdGaXrv2oLMa8QAoCz31Q0xpcdAiGAZ9E/XUAyUmQNglnou/sVk8gFFkV9WAXm/eBxGJCdPxozzZDkg+TCTNmsajhOFluW+6cb/6G7xk+4mtRT65dv6z2tzBIf4GKEhoII+JGq8uLXsUYXjFSofirt3Z0daGaSJXko5nzwgXT/N1rCA5X4Ep3WO0pcLxISTqoFt6ftMPcOUfuTMF3uzFQuf567ogqKs3vBYJ2DTDkTs1ONrRyE1/m2qDX/wXV3eRWjeuNXK6U5xhwWa5A/izV2zsom0w6Hh9PDVAKDGRZQDcVQC5TMofOMO+GlKcn7PNfndtYbYiW2xhDokG+4+qbFmsXD6VtfgtKdYaUpyfs81+d21htiJbbGEOiQb7j6psWaxcPpW1+C0p1gAA",
+ "isTaproot": true
+ },
+ {
+ "description": "Finalze taproot script-path (3-of-3)",
+ "psbt": "cHNidP8BAM8CAAAAAwPzd9k+uLSN1rgF01xY1TIA/8N+YytNZ4VP9gKFP4MyAAAAAAD/////ZtAAqL2E1fKcmGo+7xuqS+nSQeKFVKGRYaHfIvLXn4sAAAAAAP////9+h+SlCwIx1MUDT7Bek0NrWXS7xnSPi5LbYbDc9sxYIgAAAAAA/////wIgKRsAAAAAACJRIEb2SXyy8Z1Qw+npgqlQ3MhiFLAfzOQ3pCBhx72xIw0zuAUBAAAAAAAWABTJijE0v48z5ZmmfEAADXdCBcG0FAAAAAAAAQDiAgAAAAABAUfY2D1t0dyMeEH39C1yOdIxigpqm7XJNqHVT3Lc+FkiAAAAAAD+////AhIsGwAAAAAAGXapFJ5+8XZ3ZP80oFldvEwrcNsBftBmiKyYdK6xAAAAABepFLDBn59UffGbX7u/olyFDG0eG1UJhwJHMEQCIDAd3s05C61flXVFqOtov0NoHRGr8KFcOpH6R/81F46EAiBt+j9hHyvT2hYEyf8fdYsM9IgbnybtPV+kRTHDa6Rj0AEhAmmZfwmoHsmCkEOn9AfRTh+863mURelmE8hSqL4MG1EydJwgACICAi5ovBH1xLoGxPqtFh48wUEpnM+St1SbPzRwO7kBNKOQRzBEAiBpWClBybtHveXkhAgTiE8QSczMJs8MGuH4LOSNRA6s/AIgWlbB3xJOtJIsszj1qZ/whA5jK9wnTzeZzDlVs/ivq2cBAAEBK7gFAQAAAAAAIlEglCHnNLD50sRn6n3Rl8Yay0RnzcvJ9MsMVx+LY6XEDK4BE0ADaUubfpFFrzbU+vL8qCzZE/FO+9unzylfpIgQZ4HTy2qPUtLvbyH59GApdz0SiUZGl8K6Crvt9YIfI/5FxbOLARcglCHnNLD50sRn6n3Rl8Yay0RnzcvJ9MsMVx+LY6XEDK4AAQEfECcAAAAAAAAWABRPoqyhKLb53uUwnE9wBR5Jxl/XMiICA6VOpEBbPJM/xYsqO2euttYFpgec9vcxggyTyoklK660SDBFAiEAoCIktghL55iuMAmkzwYJzb+h+qmNewZXxAx/06ObxIQCIELCsBz/wd2wPlnJb27OluxMkTPnCyHA2C+SxHiX/FvPAQAAAA==",
+ "result": "cHNidP8BAM8CAAAAAwPzd9k+uLSN1rgF01xY1TIA/8N+YytNZ4VP9gKFP4MyAAAAAAD/////ZtAAqL2E1fKcmGo+7xuqS+nSQeKFVKGRYaHfIvLXn4sAAAAAAP////9+h+SlCwIx1MUDT7Bek0NrWXS7xnSPi5LbYbDc9sxYIgAAAAAA/////wIgKRsAAAAAACJRIEb2SXyy8Z1Qw+npgqlQ3MhiFLAfzOQ3pCBhx72xIw0zuAUBAAAAAAAWABTJijE0v48z5ZmmfEAADXdCBcG0FAAAAAAAAQDiAgAAAAABAUfY2D1t0dyMeEH39C1yOdIxigpqm7XJNqHVT3Lc+FkiAAAAAAD+////AhIsGwAAAAAAGXapFJ5+8XZ3ZP80oFldvEwrcNsBftBmiKyYdK6xAAAAABepFLDBn59UffGbX7u/olyFDG0eG1UJhwJHMEQCIDAd3s05C61flXVFqOtov0NoHRGr8KFcOpH6R/81F46EAiBt+j9hHyvT2hYEyf8fdYsM9IgbnybtPV+kRTHDa6Rj0AEhAmmZfwmoHsmCkEOn9AfRTh+863mURelmE8hSqL4MG1EydJwgAAEHakcwRAIgaVgpQcm7R73l5IQIE4hPEEnMzCbPDBrh+CzkjUQOrPwCIFpWwd8STrSSLLM49amf8IQOYyvcJ083mcw5VbP4r6tnASECLmi8EfXEugbE+q0WHjzBQSmcz5K3VJs/NHA7uQE0o5AAAQEruAUBAAAAAAAiUSCUIec0sPnSxGfqfdGXxhrLRGfNy8n0ywxXH4tjpcQMrgEIQgFAA2lLm36RRa821Pry/Kgs2RPxTvvbp88pX6SIEGeB08tqj1LS728h+fRgKXc9EolGRpfCugq77fWCHyP+RcWziwABAR8QJwAAAAAAABYAFE+irKEotvne5TCcT3AFHknGX9cyAQhsAkgwRQIhAKAiJLYIS+eYrjAJpM8GCc2/ofqpjXsGV8QMf9Ojm8SEAiBCwrAc/8HdsD5ZyW9uzpbsTJEz5wshwNgvksR4l/xbzwEhA6VOpEBbPJM/xYsqO2euttYFpgec9vcxggyTyoklK660AAAA",
+ "isTaproot": true
+ },
+ {
+ "description": "Finalze taproot with tapkey sig and tapscript sigs (choose tapkey)",
+ "psbt": "cHNidP8BAF4CAAAAAQrtX/VtEfTwY2iXi+s8lzx2JZbV7w9a8q6lONJ4SBm1AAAAAAD/////AZBBBgAAAAAAIlEgp+9JIlFbn3ZwqTz8p4UU73qJfQl4FvNDuyBa51FMm/kAAAAAAAEBK6BoBgAAAAAAIlEgp+9JIlFbn3ZwqTz8p4UU73qJfQl4FvNDuyBa51FMm/kBE0DqpLv0n1NC7N2okYWhHwP+mCqaDGOmNGCobgPw54CAtJdHPKY2i/ioNzq/m2Muh0hUYcLlOcU5U4xF7W+6gvRuQRQGAr5sFRVLM9rtDJmH/nCNJu0u72GXSA2wf9k4GEnXrossGVn91s+ghIb0v5fVWfWjsqU6wRJBundeShgEvDIVQNduanOw6c03gjyLWLim7b/7yet7XRp1uNrtwrMklOMSujYI/h2o+TSqS7eNwCkNddW3R6v03L6S9hWHTUYprclBFDhs6fMvKmERoTq2yrBafaRMmYoKkSuw9D4o3xjiC403iywZWf3Wz6CEhvS/l9VZ9aOypTrBEkG6d15KGAS8MhVAMKZJmsPn+pjmlWzj5oPGOj8XSs0uCMnDL1jjnosEuO7G0y9Y46GlGnfOM54w/0n2qKkdDWHpXX14F9gNesiP5EEUfKybjQL7rXdJwSP1Fb15LaJyYFrW3yJnkN+aP9IdhTOGRF1EGo0fkg5hbe3o7iwS+jTgqmGOQDsHExHbJgmL90A8hHY8WzI+xJsFXMzi5ztkZS8pXf07iNlfDnLKZYyk9ZRBkAsJ9oYsvC19Irrq27l918aqfQc6sZBiskZyMs5wQRR8rJuNAvutd0nBI/UVvXktonJgWtbfImeQ35o/0h2FM4ssGVn91s+ghIb0v5fVWfWjsqU6wRJBundeShgEvDIVQFeVntO1MSRANCT3fpEWhijVJAbF6SVBdPlNAaRKc1sn3Gz+/HTfD+f9sqRlSIsF4D+ouhv6URBtAZUCuKHW6vxiFcFB6oQ4lfHrNv47TKgSz/MUn2hgozxlIgmi2demetlGgxpSnJ+zzX53bWG2IltsYQ6JBvuPqmxZrFw+lbX4LSnWhkRdRBqNH5IOYW3t6O4sEvo04KphjkA7BxMR2yYJi/dpIHysm40C+613ScEj9RW9eS2icmBa1t8iZ5Dfmj/SHYUzrCAGAr5sFRVLM9rtDJmH/nCNJu0u72GXSA2wf9k4GEnXrrogOGzp8y8qYRGhOrbKsFp9pEyZigqRK7D0PijfGOILjTe6U5zAQhXBQeqEOJXx6zb+O0yoEs/zFJ9oYKM8ZSIJotnXpnrZRoMvbQYwKuRcSf7lDrRMU3H56Gh6NeGkogAw4hpvAn/mgCMgfKybjQL7rXdJwSP1Fb15LaJyYFrW3yJnkN+aP9IdhTOswAEXIEHqhDiV8es2/jtMqBLP8xSfaGCjPGUiCaLZ16Z62UaDARggNhmyJFW2/EeankNIJjRP9QFlju4/j/nEEcMAnlXe+XcAAA==",
+ "result": "cHNidP8BAF4CAAAAAQrtX/VtEfTwY2iXi+s8lzx2JZbV7w9a8q6lONJ4SBm1AAAAAAD/////AZBBBgAAAAAAIlEgp+9JIlFbn3ZwqTz8p4UU73qJfQl4FvNDuyBa51FMm/kAAAAAAAEBK6BoBgAAAAAAIlEgp+9JIlFbn3ZwqTz8p4UU73qJfQl4FvNDuyBa51FMm/kBCEIBQOqku/SfU0Ls3aiRhaEfA/6YKpoMY6Y0YKhuA/DngIC0l0c8pjaL+Kg3Or+bYy6HSFRhwuU5xTlTjEXtb7qC9G4AAA==",
+ "isTaproot": true
+ },
+ {
+ "description": "Finalze taproot script-path with two tapleafs signed (chooses the one with the shortest path)",
+ "psbt": "cHNidP8BAF4CAAAAAZsypn8thOjCXwg39Z1pAQCp7ndI8Jrw/8SGSmhgRiJpAAAAAAD/////AZBBBgAAAAAAIlEgFa11cf11Y+PGzl0UxS5788pld5zElAl9YY0OtTHhHzQAAAAAAAEBK6BoBgAAAAAAIlEgFa11cf11Y+PGzl0UxS5788pld5zElAl9YY0OtTHhHzRBFAUf68vx5rAfWwxMqZNce2OVVqlduXONBcm2JEPAT2Fc2gpbUnze59ZSHmflF7erkp8eUGBfJBmAcUZG5WBbC/pAJihlaGGDKsSd04fSyIk5WDACUedUvDE1C4zD80ubbX5qFzgxizY7pGG5zrrGAoW0KOEJ8TW4WEmh/WsE0erDxEEUIKvXw9clC9dJYjC2Woo/TrIhnRP9powDMmB/GF4rBRsRCNvHrz3TPp4Id2O6W4qhmerk87mqanTdLIapgqEamkDnpOB240Ce+yZlOPO3sxZDmpXhmsxtIl6a/R6TS2ekf2VCdZXg4Dqy3OpjzxQ/jDMbXbzETL2mdHlsgQnBnUcoYhXBr6rSntatZCCpyXS0zh+WAkLdMI4XHA0rVYAbFyHRzrqYKqOqTjCjec9FzSPuoLS+tXB4VvMgzUTs3kZS/bDfqlRY6TCnm2dtB3ED1fA6rlsUAqbWuurDp2W9ONMZj7NiIyAFH+vL8eawH1sMTKmTXHtjlVapXblzjQXJtiRDwE9hXKzAohXBr6rSntatZCCpyXS0zh+WAkLdMI4XHA0rVYAbFyHRzrq0JN6gn4QLkyoANzzc29JWULjDrP5UqfSmQaKGchuNJtrHlXZrvaHq6qReW/oKlQ/dX0xKraWx8wgu3JaJuf0K7iijuCxK7L69N4En3IjdGeHqk+26eie3VOHIgdMIh0q9q7FEmRK2sbmE7NhWpbGiU/lLBm6XT+D9HvPO+T63rSMgIKvXw9clC9dJYjC2Woo/TrIhnRP9powDMmB/GF4rBRuswAAA",
+ "result": "cHNidP8BAF4CAAAAAZsypn8thOjCXwg39Z1pAQCp7ndI8Jrw/8SGSmhgRiJpAAAAAAD/////AZBBBgAAAAAAIlEgFa11cf11Y+PGzl0UxS5788pld5zElAl9YY0OtTHhHzQAAAAAAAEBK6BoBgAAAAAAIlEgFa11cf11Y+PGzl0UxS5788pld5zElAl9YY0OtTHhHzQBCMcDQCYoZWhhgyrEndOH0siJOVgwAlHnVLwxNQuMw/NLm21+ahc4MYs2O6Rhuc66xgKFtCjhCfE1uFhJof1rBNHqw8QiIAUf68vx5rAfWwxMqZNce2OVVqlduXONBcm2JEPAT2FcrGHBr6rSntatZCCpyXS0zh+WAkLdMI4XHA0rVYAbFyHRzrqYKqOqTjCjec9FzSPuoLS+tXB4VvMgzUTs3kZS/bDfqlRY6TCnm2dtB3ED1fA6rlsUAqbWuurDp2W9ONMZj7NiAAA=",
+ "isTaproot": true
+ },
+ {
+ "description": "Finalze taproot script-path with two tapleafs signed (explicitly choose leaf)",
+ "psbt": "cHNidP8BAF4CAAAAAZsypn8thOjCXwg39Z1pAQCp7ndI8Jrw/8SGSmhgRiJpAAAAAAD/////AZBBBgAAAAAAIlEgFa11cf11Y+PGzl0UxS5788pld5zElAl9YY0OtTHhHzQAAAAAAAEBK6BoBgAAAAAAIlEgFa11cf11Y+PGzl0UxS5788pld5zElAl9YY0OtTHhHzRBFAUf68vx5rAfWwxMqZNce2OVVqlduXONBcm2JEPAT2Fc2gpbUnze59ZSHmflF7erkp8eUGBfJBmAcUZG5WBbC/pAJihlaGGDKsSd04fSyIk5WDACUedUvDE1C4zD80ubbX5qFzgxizY7pGG5zrrGAoW0KOEJ8TW4WEmh/WsE0erDxEEUIKvXw9clC9dJYjC2Woo/TrIhnRP9powDMmB/GF4rBRsRCNvHrz3TPp4Id2O6W4qhmerk87mqanTdLIapgqEamkDnpOB240Ce+yZlOPO3sxZDmpXhmsxtIl6a/R6TS2ekf2VCdZXg4Dqy3OpjzxQ/jDMbXbzETL2mdHlsgQnBnUcoYhXBr6rSntatZCCpyXS0zh+WAkLdMI4XHA0rVYAbFyHRzrqYKqOqTjCjec9FzSPuoLS+tXB4VvMgzUTs3kZS/bDfqlRY6TCnm2dtB3ED1fA6rlsUAqbWuurDp2W9ONMZj7NiIyAFH+vL8eawH1sMTKmTXHtjlVapXblzjQXJtiRDwE9hXKzAohXBr6rSntatZCCpyXS0zh+WAkLdMI4XHA0rVYAbFyHRzrq0JN6gn4QLkyoANzzc29JWULjDrP5UqfSmQaKGchuNJtrHlXZrvaHq6qReW/oKlQ/dX0xKraWx8wgu3JaJuf0K7iijuCxK7L69N4En3IjdGeHqk+26eie3VOHIgdMIh0q9q7FEmRK2sbmE7NhWpbGiU/lLBm6XT+D9HvPO+T63rSMgIKvXw9clC9dJYjC2Woo/TrIhnRP9powDMmB/GF4rBRuswAAA",
+ "result": "cHNidP8BAF4CAAAAAZsypn8thOjCXwg39Z1pAQCp7ndI8Jrw/8SGSmhgRiJpAAAAAAD/////AZBBBgAAAAAAIlEgFa11cf11Y+PGzl0UxS5788pld5zElAl9YY0OtTHhHzQAAAAAAAEBK6BoBgAAAAAAIlEgFa11cf11Y+PGzl0UxS5788pld5zElAl9YY0OtTHhHzQBCMcDQCYoZWhhgyrEndOH0siJOVgwAlHnVLwxNQuMw/NLm21+ahc4MYs2O6Rhuc66xgKFtCjhCfE1uFhJof1rBNHqw8QiIAUf68vx5rAfWwxMqZNce2OVVqlduXONBcm2JEPAT2FcrGHBr6rSntatZCCpyXS0zh+WAkLdMI4XHA0rVYAbFyHRzrqYKqOqTjCjec9FzSPuoLS+tXB4VvMgzUTs3kZS/bDfqlRY6TCnm2dtB3ED1fA6rlsUAqbWuurDp2W9ONMZj7NiAAA=",
+ "isTaproot": true
}
],
"extractor": [
@@ -338,6 +473,51 @@
"index": 2
},
"equals": "cHNidP8BADMCAAAAAQABAgMEBQYHCAkKCwwNDg8AAQIDBAUGBwgJCgsMDQ4PAgAAAAD/////AAAAAAAAAAA="
+ },
+ {
+ "description": "checks for mixed taproot and non-taproot fields",
+ "inputData": {
+ "hash": "Buffer.from('000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f', 'hex')",
+ "index": 0,
+ "redeemScript": "Buffer.from('0014000102030405060708090a0b0c0d0e0f00010000', 'hex')",
+ "tapInternalKey": "Buffer.from('000102030405060708090a0b0c0d0e0f00010203040506070000000000000000', 'hex')"
+ },
+ "exception": "Invalid arguments for Psbt.addInput. Cannot use both taproot and non-taproot fields."
+ },
+ {
+ "description": "checks for tapleaf in taptree",
+ "inputData": {
+ "hash": "Buffer.from('000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f', 'hex')",
+ "index": 0,
+ "tapMerkleRoot": "Buffer.from('5cf5873456b400364b18bdc9a1a233870fa622a6010deef1d4e08474f3a103b4', 'hex')",
+ "tapLeafScript": [
+ {
+ "leafVersion": 192,
+ "script": "Buffer.from('20d5e347235eba74ae0cec686b668d5a9432f45a555d6ab22cebf5974bde9dc4f3ac', 'hex')",
+ "controlBlock": "Buffer.from('c0720768b9946ac22371653d92cc343bf109b8a3f819e231159fd4f2b1328944ecb424dea09f840b932a00373cdcdbd25650b8c3acfe54a9f4a641a286721b8d26dac795766bbda1eaeaa45e5bfa0a950fdd5f4c4aada5b1f3082edc9689b9fd0aee28a3b82c4aecbebd378127dc88dd19e1ea93edba7a27b754e1c881d308874a7ac4489544b7b840d045599ec9415ae71ccb090c2c465c05c9fa05f41c7a06d5', 'hex')"
+ }
+ ]
+ },
+ "exception": "Invalid arguments for Psbt.addInput. Tapleaf not part of taptree."
+ }
+ ]
+ },
+ "updateInput": {
+ "checks": [
+ {
+ "description": "checks for new tapleaf in taptree",
+ "psbt": "cHNidP8BADMCAAAAAQ1YzlMrOEvTUad+B8zippgoNUwrXhwvsHMMGwZb6S7LAAAAAAD/////AAAAAAAAAQEroGgGAAAAAAAiUSD3Vv2gTbCrHTNlF18uY/YKLdATh0ph4pwrCyUHkhtZngEYIHsGySMEjYsxl3fCSP6Ap7tG06AUPLXA8nGnwzSEH70VAAA=",
+ "index": 0,
+ "inputData": {
+ "tapLeafScript": [
+ {
+ "leafVersion": 192,
+ "script": "Buffer.from('20d5e347235eba74ae0cec686b668d5a9432f45a555d6ab22cebf5974bde9dc4f3ac', 'hex')",
+ "controlBlock": "Buffer.from('c0720768b9946ac22371653d92cc343bf109b8a3f819e231159fd4f2b1328944ecb424dea09f840b932a00373cdcdbd25650b8c3acfe54a9f4a641a286721b8d26dac795766bbda1eaeaa45e5bfa0a950fdd5f4c4aada5b1f3082edc9689b9fd0aee28a3b82c4aecbebd378127dc88dd19e1ea93edba7a27b754e1c881d308874a7ac4489544b7b840d045599ec9415ae71ccb090c2c465c05c9fa05f41c7a06d5', 'hex')"
+ }
+ ]
+ },
+ "exception": "Invalid arguments for Psbt.updateInput. Tapleaf not part of taptree."
}
]
},
@@ -351,12 +531,133 @@
},
"exception": "Error adding output."
},
+ {
+ "description": "Checks the mandatory values for adding an output",
+ "outputData": {
+ "value": 1000
+ },
+ "exception": "Invalid arguments for Psbt\\.addOutput. Requires single object with at least \\[script or address\\] and \\[value\\]"
+ },
{
"description": "Adds output normally",
"outputData": {
"address": "1P2NFEBp32V2arRwZNww6tgXEV58FG94mr",
"value": 42
}
+ },
+ {
+ "description": "Adds taproot output with internal tap key and tap tree",
+ "isTaproot": true,
+ "psbt": "cHNidP8BADMCAAAAAcbgSGx76du9GXsr4c6Yk7DFglfHi7M2jdCNUXwc+Q+EAAAAAAD/////AAAAAAAAAQEroGgGAAAAAAAiUSClLxmVQ6aZXLEwkYA/WGZuIE91BHT7xP7DIEAvz/NITaIVwd2NnolThxX+QCpgFksj8u93bzuZy6olaHxJNHaArwK1tCTeoJ+EC5MqADc83NvSVlC4w6z+VKn0pkGihnIbjSbax5V2a72h6uqkXlv6CpUP3V9MSq2lsfMILtyWibn9Cu4oo7gsSuy+vTeBJ9yI3Rnh6pPtunont1ThyIHTCIdKesRIlUS3uEDQRVmeyUFa5xzLCQwsRlwFyfoF9Bx6BtUjIFX368Cp2d6uXSbyrn23vC4KMjRRlEssuTTh7ThM6bXQrMAAAA==",
+ "outputData": {
+ "address": "bc1px4ssshedlz4jc56ses3lftz462a06jwy8my4pwpx6twx30vvv6nsgwcpu3",
+ "value": 410000,
+ "tapInternalKey": "Buffer.from('f6d4ce132444de7f0e3a1d2be9b38ceec798cf9a76eeeac585869445830eb167', 'hex')",
+ "tapTree": {
+ "leaves": [
+ {
+ "depth": 2,
+ "leafVersion": 192,
+ "script": "Buffer.from('2050929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0ac', 'hex')"
+ },
+ {
+ "depth": 3,
+ "leafVersion": 192,
+ "script": "Buffer.from('2050929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac1ac', 'hex')"
+ },
+ {
+ "depth": 3,
+ "leafVersion": 192,
+ "script": "Buffer.from('202258b1c3160be0864a541854eec9164a572f094f7562628281a8073bb89173a7ac', 'hex')"
+ },
+ {
+ "depth": 2,
+ "leafVersion": 192,
+ "script": "Buffer.from('2050929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac2ac', 'hex')"
+ },
+ {
+ "depth": 3,
+ "leafVersion": 192,
+ "script": "Buffer.from('2050929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac3ac', 'hex')"
+ },
+ {
+ "depth": 4,
+ "leafVersion": 192,
+ "script": "Buffer.from('2050929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac4ac', 'hex')"
+ },
+ {
+ "depth": 4,
+ "leafVersion": 192,
+ "script": "Buffer.from('2055f7ebc0a9d9deae5d26f2ae7db7bc2e0a323451944b2cb934e1ed384ce9b5d0ac', 'hex')"
+ }
+ ]
+ }
+ },
+ "result": "cHNidP8BAF4CAAAAAcbgSGx76du9GXsr4c6Yk7DFglfHi7M2jdCNUXwc+Q+EAAAAAAD/////AZBBBgAAAAAAIlEgNWEIXy34qyxTUMwj9KxV0rr9ScQ+yVC4JtLcaL2MZqcAAAAAAAEBK6BoBgAAAAAAIlEgpS8ZlUOmmVyxMJGAP1hmbiBPdQR0+8T+wyBAL8/zSE2iFcHdjZ6JU4cV/kAqYBZLI/Lvd287mcuqJWh8STR2gK8CtbQk3qCfhAuTKgA3PNzb0lZQuMOs/lSp9KZBooZyG40m2seVdmu9oerqpF5b+gqVD91fTEqtpbHzCC7clom5/QruKKO4LErsvr03gSfciN0Z4eqT7bp6J7dU4ciB0wiHSnrESJVEt7hA0EVZnslBWuccywkMLEZcBcn6BfQcegbVIyBV9+vAqdnerl0m8q59t7wuCjI0UZRLLLk04e04TOm10KzAAAEFIPbUzhMkRN5/DjodK+mzjO7HmM+adu7qxYWGlEWDDrFnAQb9AwECwCIgUJKbdMGgSVS3i0tgNel6XgeKWg8o7JbVR7/ums6AOsCsA8AiIFCSm3TBoElUt4tLYDXpel4HiloPKOyW1Ue/7prOgDrBrAPAIiAiWLHDFgvghkpUGFTuyRZKVy8JT3ViYoKBqAc7uJFzp6wCwCIgUJKbdMGgSVS3i0tgNel6XgeKWg8o7JbVR7/ums6AOsKsA8AiIFCSm3TBoElUt4tLYDXpel4HiloPKOyW1Ue/7prOgDrDrATAIiBQkpt0waBJVLeLS2A16XpeB4paDyjsltVHv+6azoA6xKwEwCIgVffrwKnZ3q5dJvKufbe8LgoyNFGUSyy5NOHtOEzptdCsAA=="
+ },
+ {
+ "description": "Adds taproot output with internal tap key and correct address",
+ "isTaproot": true,
+ "psbt": "cHNidP8BAFwCAAAAAk9Ve2845C8v//JR71uKzf70FjeNfx7SvB4l3A+Q44UKAAAAAAD/////FIp10hu+RPgTSFGPmjwb01Tf/a5UkcPFUpOw/X6UEPYAAAAAAP////8AAAAAAAABASugaAYAAAAAACJRINqadqaDhePTT29qdKQScUmyJxEeDw12HLjkMYMVSlnkARcgDN4bJzix3HdE8+FUM6UgA1lEKpgpIImVUePmAGYh7yYAAQDAAgAAAAFbIoaAJqDd/f4mZppgYV4UR9bgU8lT9pTH4/aDJdFUWgAAAABrSDBFAiEA6JS0xMdSQhG+9gAPSxGs6HazyauyNUBMwrmF386IAxwCIGiyH9QHCzKOBTtg+VsISF4nUi9NfAtJAtC02J03+I07ASEDu1tkEI8W1bd6qrPZ3uLaHvE90BDUvRCwvTTzPYPXzwf/////AaBoBgAAAAAAGXapFL5QEf51k4TA7Mp5d18IM2ddq79oiKwAAAAAAAA=",
+ "outputData": {
+ "tapInternalKey": "Buffer.from('884d969439deced21d1ab71ecd9ef9a6a8795215588ce7eff4ad5efc903e40ec', 'hex')",
+ "address": "bc1p74zfvfd7rndcn8lvzuec4hj4kzp9nnjvapvx5fgtqsegzz656hhsee8kwu",
+ "value": 410000
+ },
+ "result": "cHNidP8BAIcCAAAAAk9Ve2845C8v//JR71uKzf70FjeNfx7SvB4l3A+Q44UKAAAAAAD/////FIp10hu+RPgTSFGPmjwb01Tf/a5UkcPFUpOw/X6UEPYAAAAAAP////8BkEEGAAAAAAAiUSD1RJYlvhzbiZ/sFzOK3lWwglnOTOhYaiULBDKBC1TV7wAAAAAAAQEroGgGAAAAAAAiUSDamnamg4Xj009vanSkEnFJsicRHg8Ndhy45DGDFUpZ5AEXIAzeGyc4sdx3RPPhVDOlIANZRCqYKSCJlVHj5gBmIe8mAAEAwAIAAAABWyKGgCag3f3+JmaaYGFeFEfW4FPJU/aUx+P2gyXRVFoAAAAAa0gwRQIhAOiUtMTHUkIRvvYAD0sRrOh2s8mrsjVATMK5hd/OiAMcAiBosh/UBwsyjgU7YPlbCEheJ1IvTXwLSQLQtNidN/iNOwEhA7tbZBCPFtW3eqqz2d7i2h7xPdAQ1L0QsL008z2D188H/////wGgaAYAAAAAABl2qRS+UBH+dZOEwOzKeXdfCDNnXau/aIisAAAAAAABBSCITZaUOd7O0h0atx7NnvmmqHlSFViM5+/0rV78kD5A7AA="
+ },
+ {
+ "description": "Adds taproot output with internal tap key and bad address",
+ "isTaproot": true,
+ "psbt": "cHNidP8BAFwCAAAAAk9Ve2845C8v//JR71uKzf70FjeNfx7SvB4l3A+Q44UKAAAAAAD/////FIp10hu+RPgTSFGPmjwb01Tf/a5UkcPFUpOw/X6UEPYAAAAAAP////8AAAAAAAABASugaAYAAAAAACJRINqadqaDhePTT29qdKQScUmyJxEeDw12HLjkMYMVSlnkARcgDN4bJzix3HdE8+FUM6UgA1lEKpgpIImVUePmAGYh7yYAAQDAAgAAAAFbIoaAJqDd/f4mZppgYV4UR9bgU8lT9pTH4/aDJdFUWgAAAABrSDBFAiEA6JS0xMdSQhG+9gAPSxGs6HazyauyNUBMwrmF386IAxwCIGiyH9QHCzKOBTtg+VsISF4nUi9NfAtJAtC02J03+I07ASEDu1tkEI8W1bd6qrPZ3uLaHvE90BDUvRCwvTTzPYPXzwf/////AaBoBgAAAAAAGXapFL5QEf51k4TA7Mp5d18IM2ddq79oiKwAAAAAAAA=",
+ "outputData": {
+ "tapInternalKey": "Buffer.from('884d969439deced21d1ab71ecd9ef9a6a8795215588ce7eff4ad5efc903e40ec', 'hex')",
+ "address": "bc1p3efq8ujsj0qr5xvms7mv89p8cz0crqdtuxe9ms6grqgxc9sgsntslthf6w",
+ "value": 410000
+ },
+ "exception": "Error adding output. Script or address mismatch."
+ },
+ {
+ "description": "Adds taproot output with both taproot and non-taproot fields",
+ "isTaproot": true,
+ "psbt": "cHNidP8BAFwCAAAAAk9Ve2845C8v//JR71uKzf70FjeNfx7SvB4l3A+Q44UKAAAAAAD/////FIp10hu+RPgTSFGPmjwb01Tf/a5UkcPFUpOw/X6UEPYAAAAAAP////8AAAAAAAABASugaAYAAAAAACJRINqadqaDhePTT29qdKQScUmyJxEeDw12HLjkMYMVSlnkARcgDN4bJzix3HdE8+FUM6UgA1lEKpgpIImVUePmAGYh7yYAAQDAAgAAAAFbIoaAJqDd/f4mZppgYV4UR9bgU8lT9pTH4/aDJdFUWgAAAABrSDBFAiEA6JS0xMdSQhG+9gAPSxGs6HazyauyNUBMwrmF386IAxwCIGiyH9QHCzKOBTtg+VsISF4nUi9NfAtJAtC02J03+I07ASEDu1tkEI8W1bd6qrPZ3uLaHvE90BDUvRCwvTTzPYPXzwf/////AaBoBgAAAAAAGXapFL5QEf51k4TA7Mp5d18IM2ddq79oiKwAAAAAAAA=",
+ "outputData": {
+ "address": "bc1px4ssshedlz4jc56ses3lftz462a06jwy8my4pwpx6twx30vvv6nsgwcpu3",
+ "value": 410000,
+ "tapInternalKey": "Buffer.from('884d969439deced21d1ab71ecd9ef9a6a8795215588ce7eff4ad5efc903e40ec', 'hex')",
+ "redeemScript": "Buffer.from('5221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae', 'hex')"
+ },
+ "exception": "Invalid arguments for Psbt.addOutput. Cannot use both taproot and non-taproot fields."
+ },
+ {
+ "description": "Adds output after tapkey signature has been added",
+ "isTaproot": true,
+ "psbt": "cHNidP8BAIcCAAAAAvz6EyMK2VQ0DFKWZyIyGZLZCnaIDtG5RI55OrGVs6u7AAAAAAD/////oB9A2l2NN5WPNjBDaRsz9Nmla1WdM3jl96ElSV5WWHMAAAAAAP////8BkEEGAAAAAAAiUSCnucwjGG2x9zGqP+E0PwxTkptd/9AYMW12ch7ffaogBAAAAAAAAQEroGgGAAAAAAAiUSDbRhL5phuWYLyqKbSqVb/Pic2mpPL46idVKQur9i06ugETQOPidQWL9dHDxd819Pn5QNNGyH0fmDv58pexSJVDalZ0W/HPVliPFZy7s//0TfTbvFce87/nKXcrwz5YuItIY6cBFyAOgJIoTQyYcpAqNfsK9CutgWckFE3mBMgpL50lKXb7oQABAL8CAAAAAcdkwPNWdYy1O8QM1xRkrJjas72QGQPEanYEpv6iT3PXAAAAAGpHMEQCIFpl6Nx8hoZA1o5Huo958RS/Hndy+DloLhp0nvRyBzN8AiB9jEbitThSs6TZ1XVBHZebj4nZbLMg3CCtss8WSZpv7QEhA+VxIxwCWeqoeDMYcEw3EC0ZPxIkOqt0YSK9Y3hAKTd1/////wGgaAYAAAAAABl2qRS9gA7n636cw49cUBJWm7IDJPKuuoisAAAAACICAknGKVC7w5Ek4l3IK5XCUwLNCeU4DD+76cdMAYDB72g3RzBEAiAjekFbBddaHY/LZidwKd3sBbifjmbEpka2Ps0B2Crg+wIgUoN4BhGCwMRTDaHbAX+0MXSGYA9nSlqeQJXpWQRCBfQBAAEFIE6DvlLK0f4BrtpMgez01WQYKLUQZ8HtbfuDPD52hmSvAA==",
+ "outputData": {
+ "address": "bc1px4ssshedlz4jc56ses3lftz462a06jwy8my4pwpx6twx30vvv6nsgwcpu3",
+ "value": 410000
+ },
+ "exception": "Can not modify transaction, signatures exist."
+ },
+ {
+ "description": "Adds output after tap key input has been finalized",
+ "isTaproot": true,
+ "psbt": "cHNidP8BAF4CAAAAAfyhtBTm+3OPYBuMHvdPMf9jqniZDY925hbmnt8hxbA6AAAAAAD/////AZBBBgAAAAAAIlEgV6CyzSs5a6Tc8A+TYOiozWnh6FRH9E/VWFanhMAEbLEAAAAAAAEBK6BoBgAAAAAAIlEgV6CyzSs5a6Tc8A+TYOiozWnh6FRH9E/VWFanhMAEbLEBCEIBQOAqWBD/2jhPWzQvesT8sjkN2Cowphp3QvmlWbHiLx753ChcUovvWyBlWiCq77Kk+lZGEhC4vjClSjc26br+dc8AAA==",
+ "outputData": {
+ "address": "bc1px4ssshedlz4jc56ses3lftz462a06jwy8my4pwpx6twx30vvv6nsgwcpu3",
+ "value": 410000
+ },
+ "exception": "Can not modify transaction, signatures exist."
+ },
+ {
+ "description": "Adds output after tapscript signatures have been added",
+ "isTaproot": true,
+ "psbt": "cHNidP8BAF4CAAAAAa/0mhnSBXdEBKbbMC+2hm6AZZtCLBxBeubd1sjtau5dAAAAAAD/////AZBBBgAAAAAAIlEgISRIfamb9rCYzad52ikfoUUuFlvyTcImZMavR0jEaUQAAAAAAAEBK6BoBgAAAAAAIlEgISRIfamb9rCYzad52ikfoUUuFlvyTcImZMavR0jEaURBFBnEcOpwiHjNYPtWJOrQ8Pgc9bxBKyZh/i2D837Z1rC8BibL1C4Z/5e6dKzWfkzpsIbE5WEVn1bYpAAjrqIKMHlAKkl3w3Gfpkl9b0yDVbTlZd4yCEL9V2DJs6zpPrEmn3wiohBy8wwE6EZ0FxQdrCupnHKXhHBjpcHVwfJRQfcy9EEULx1ijisiHgGb/9/hBNhsIOv1ZyWsfmi/Ql+oz7AOuqAGJsvULhn/l7p0rNZ+TOmwhsTlYRWfVtikACOuogoweUBKNkxBf6vT8m7ISt1WikLWW9udCP7OQLXztr1IPalJT5z+esAWmgeLS7QoLgzTu8AnYp/rHxsgZ6CgiV8tlkciQRRXE7VxCk67h7Ee6CbSgNyotChx7CgwNTfxdJkyvCS0DgYmy9QuGf+XunSs1n5M6bCGxOVhFZ9W2KQAI66iCjB5QFN6DGtLlSIFBjZbdh3rbKBtBcEDSiEcuVxnSPpdM1RnQRmw5Ujo+/76wZfmGBMFzV0IA7vnHzzXN73jT6O8/wJiFcDBdB6IhNxYUBYgZT1K7FG5SblQ3S6nQMKRLc2vPcA0BhpSnJ+zzX53bWG2IltsYQ6JBvuPqmxZrFw+lbX4LSnWGlKcn7PNfndtYbYiW2xhDokG+4+qbFmsXD6VtfgtKdZpIFcTtXEKTruHsR7oJtKA3Ki0KHHsKDA1N/F0mTK8JLQOrCAvHWKOKyIeAZv/3+EE2Gwg6/VnJax+aL9CX6jPsA66oLogGcRw6nCIeM1g+1Yk6tDw+Bz1vEErJmH+LYPzftnWsLy6U5zAAAA=",
+ "outputData": {
+ "address": "bc1px4ssshedlz4jc56ses3lftz462a06jwy8my4pwpx6twx30vvv6nsgwcpu3",
+ "value": 410000
+ },
+ "exception": "Can not modify transaction, signatures exist."
}
]
},
@@ -459,10 +760,45 @@
"description": "allows signing non-whitelisted sighashtype when explicitly passed in",
"shouldSign": {
"psbt": "cHNidP8BADMBAAAAAYaq+PdOUY2PnV9kZKa82XlqrPByOqwH2TRg2+LQdqs2AAAAAAD/////AAAAAAAAAQEgAOH1BQAAAAAXqRSTNeWHqa9INvSnQ120SZeJc+6JSocBAwSBAAAAAQQWABQvLKRyDqYsPYImhD3eURpDGL10RwAA",
- "sighashTypes": [129],
+ "sighashTypes": [
+ 129
+ ],
"inputToCheck": 0,
"WIF": "KxnAnQh6UJBxLF8Weup77yn8tWhLHhDhnXeyJuzmmcZA5aRdMJni"
}
+ },
+ {
+ "description": "checks taproot signing works when using custom signature type of 65 bytes",
+ "isTaproot": true,
+ "shouldSign": {
+ "psbt": "cHNidP8BAF4CAAAAAf17fGksrz9eKGx1nSU3RX4vcwr7bfNdQzPZ9dSEkWBcAAAAAAD/////AZBBBgAAAAAAIlEgPLBe/d3922lmXjTIt52b9NG1HFDC9jzPCTn111AG8TQAAAAAAAEBK6BoBgAAAAAAIlEgPLBe/d3922lmXjTIt52b9NG1HFDC9jzPCTn111AG8TQBAwSDAAAAARcgWzC4qnD37J3WaDEbZPRihBXdI0gN68BGutJykDcHR6wBGCDlDrX1cnzwZvmcyLBH8M6NiS9lk7JVwM58wZZVOzmuMwAA",
+ "sighashTypes": [
+ 131
+ ],
+ "inputToCheck": 0,
+ "WIF": "KypUz2y1jdgzM8HGDUx9DYLmyzd8EWhruvLX2J5iSL7MiAcc7dBG",
+ "result": "cHNidP8BAF4CAAAAAf17fGksrz9eKGx1nSU3RX4vcwr7bfNdQzPZ9dSEkWBcAAAAAAD/////AZBBBgAAAAAAIlEgPLBe/d3922lmXjTIt52b9NG1HFDC9jzPCTn111AG8TQAAAAAAAEBK6BoBgAAAAAAIlEgPLBe/d3922lmXjTIt52b9NG1HFDC9jzPCTn111AG8TQBAwSDAAAAARNB00Q0N1Uq9JJ5oXGHRK31Juc5o1kKiWLla9ygRWjqItHETXP4frDFThlXShPu/3ltLKVRHXd20zJVr3r/1BgcF4MBFyBbMLiqcPfsndZoMRtk9GKEFd0jSA3rwEa60nKQNwdHrAEYIOUOtfVyfPBm+ZzIsEfwzo2JL2WTslXAznzBllU7Oa4zAAA="
+ }
+ },
+ {
+ "description": "checks taproot key-path signer (tweaked key) matches internal tap key",
+ "isTaproot": true,
+ "shouldSign": {
+ "psbt": "cHNidP8BAF4CAAAAAf17fGksrz9eKGx1nSU3RX4vcwr7bfNdQzPZ9dSEkWBcAAAAAAD/////AZBBBgAAAAAAIlEgPLBe/d3922lmXjTIt52b9NG1HFDC9jzPCTn111AG8TQAAAAAAAEBK6BoBgAAAAAAIlEgPLBe/d3922lmXjTIt52b9NG1HFDC9jzPCTn111AG8TQBFyBbMLiqcPfsndZoMRtk9GKEFd0jSA3rwEa60nKQNwdHrAEYIOUOtfVyfPBm+ZzIsEfwzo2JL2WTslXAznzBllU7Oa4zAAA=",
+ "inputToCheck": 0,
+ "WIF": "KypUz2y1jdgzM8HGDUx9DYLmyzd8EWhruvLX2J5iSL7MiAcc7dBG",
+ "result": "cHNidP8BAF4CAAAAAf17fGksrz9eKGx1nSU3RX4vcwr7bfNdQzPZ9dSEkWBcAAAAAAD/////AZBBBgAAAAAAIlEgPLBe/d3922lmXjTIt52b9NG1HFDC9jzPCTn111AG8TQAAAAAAAEBK6BoBgAAAAAAIlEgPLBe/d3922lmXjTIt52b9NG1HFDC9jzPCTn111AG8TQBE0CgvdrzBha8PBO1WyIR8VempGnHK9tlU7ehwQimbpUzjK+cqOsSl1wnszN9KXsM0pF7WbojsVM/WJdmTMllB7fQARcgWzC4qnD37J3WaDEbZPRihBXdI0gN68BGutJykDcHR6wBGCDlDrX1cnzwZvmcyLBH8M6NiS9lk7JVwM58wZZVOzmuMwAA"
+ }
+ },
+ {
+ "description": "check taproot script-path signer found for the input",
+ "isTaproot": true,
+ "shouldSign": {
+ "psbt": "cHNidP8BAF4CAAAAAUYAJ/rjwsScVTFXg1in8cNdaBDtwLAsQ6l1O8sWijmlAAAAAAD/////AZBBBgAAAAAAIlEgFlrANpbJgyRLzVMhozrVHKr6rGrz7e2bQ+3TyITRhB4AAAAAAAEBK6BoBgAAAAAAIlEgFlrANpbJgyRLzVMhozrVHKr6rGrz7e2bQ+3TyITRhB5BFNs7WfGuA1DZ7ZorvaC77E4rn/I2hbVtogxHpEnxKZ3DGlKcn7PNfndtYbYiW2xhDokG+4+qbFmsXD6VtfgtKdZAbjBxWKodWLnyqPP18sLAjhTc/OXtHVtk+Abc/e8SoTIxaOORlmqOegbCKAUyL4+NFdAlgtcUyHUCxWaxJ3ykxGIVwOpmBmLM44rn+zwtvl1cyWYheMCVYuX2agqeiHOvsrXIGlKcn7PNfndtYbYiW2xhDokG+4+qbFmsXD6VtfgtKdYaUpyfs81+d21htiJbbGEOiQb7j6psWaxcPpW1+C0p1mkgq1snxZRfWXi8CMjFSaobG3elbWWeL9QqflyaQtTYEG2sINs7WfGuA1DZ7ZorvaC77E4rn/I2hbVtogxHpEnxKZ3DuiB8Z8sVUSbQp0q8mQiD5n5coMrRzQAASnO9NspgIk9pZ7pTnMAAAA==",
+ "inputToCheck": 0,
+ "WIF": "L2wCzcNaJwG1W9djnumJnPQZTCpfeCkR2wgwfupphmThSrwTMCR6",
+ "result": "cHNidP8BAF4CAAAAAUYAJ/rjwsScVTFXg1in8cNdaBDtwLAsQ6l1O8sWijmlAAAAAAD/////AZBBBgAAAAAAIlEgFlrANpbJgyRLzVMhozrVHKr6rGrz7e2bQ+3TyITRhB4AAAAAAAEBK6BoBgAAAAAAIlEgFlrANpbJgyRLzVMhozrVHKr6rGrz7e2bQ+3TyITRhB5BFKtbJ8WUX1l4vAjIxUmqGxt3pW1lni/UKn5cmkLU2BBtT3q/MbhvStFL2jaKZM6N8TeYvCUfSVytPrzfjkXWQbdA7g4pNghfGa8m3iusZD4LGZWuvoc9ysHg8RrQuuaFUQvHGpzgmFUJ7LwDYhtbul/i4m6iGU3ArEqLGO5JxcLrgUEU2ztZ8a4DUNntmiu9oLvsTiuf8jaFtW2iDEekSfEpncMaUpyfs81+d21htiJbbGEOiQb7j6psWaxcPpW1+C0p1kBuMHFYqh1YufKo8/XywsCOFNz85e0dW2T4Btz97xKhMjFo45GWao56BsIoBTIvj40V0CWC1xTIdQLFZrEnfKTEYhXA6mYGYszjiuf7PC2+XVzJZiF4wJVi5fZqCp6Ic6+ytcgaUpyfs81+d21htiJbbGEOiQb7j6psWaxcPpW1+C0p1hpSnJ+zzX53bWG2IltsYQ6JBvuPqmxZrFw+lbX4LSnWaSCrWyfFlF9ZeLwIyMVJqhsbd6VtZZ4v1Cp+XJpC1NgQbawg2ztZ8a4DUNntmiu9oLvsTiuf8jaFtW2iDEekSfEpncO6IHxnyxVRJtCnSryZCIPmflygytHNAABKc702ymAiT2lnulOcwAAA"
+ }
}
]
},
@@ -512,6 +848,14 @@
}
]
},
+ "finalizeInput": {
+ "finalizeTapleafByHash": {
+ "psbt": "cHNidP8BAF4CAAAAAXbYuDMSrbXuy1CXHZaiA8jjI+wcNkkCOL2dJD2dNf3kAAAAAAD/////AZBBBgAAAAAAIlEgcJi/YJDTvc6x6mRTIYgQG/+fEmLs2RjsO/FHW5okOEsAAAAAAAEBK6BoBgAAAAAAIlEgcJi/YJDTvc6x6mRTIYgQG/+fEmLs2RjsO/FHW5okOEtBFG78QHu9pQ+jR/B1+dz9XQtU0jFgoC5AFXcvibFi1jFAEkntiZ6MdFzn0seJJapRqACzEKXd0ZlMa34/iq0HnrVAfYaCk2E35rtXsMfbr/Hrbk/BtLF2VLepE0vIIB3UOWZEUF8akmlnL4Kh0xwMn4Cp99QivnI0B3XOsYfEznfDHUEUesul6blPS+qc/rm41L9nGaAck9KDAZAutbDsNLSZuXXMWh7785JCWLEpzsZRkM/9RJ+EaLYT/0O2sg4cHNHo1EBHFn/xRZno2fX/vLc1l2qHqZGB2vbLkZDj5KsVfqeJd78uIc5b6uAG0K+BBtq2H6HUvl+8RWd2GWmVoMTYppVNYhXAjGyV+gMPwhuPgQ05yTUt2dmE2xJLdkeHBaiap2ej2SuYKqOqTjCjec9FzSPuoLS+tXB4VvMgzUTs3kZS/bDfqvuLvWTUb+e6sUIP9JrqDAHAjkYMd3K3PTfk+B6aMTv4IyB6y6XpuU9L6pz+ubjUv2cZoByT0oMBkC61sOw0tJm5dazAohXAjGyV+gMPwhuPgQ05yTUt2dmE2xJLdkeHBaiap2ej2Su0JN6gn4QLkyoANzzc29JWULjDrP5UqfSmQaKGchuNJtrHlXZrvaHq6qReW/oKlQ/dX0xKraWx8wgu3JaJuf0K7iijuCxK7L69N4En3IjdGeHqk+26eie3VOHIgdMIh0q48S7pX3ESdKuEJ4Jo9s+PwRFNvGdokOHBSefrgpE1JCMgbvxAe72lD6NH8HX53P1dC1TSMWCgLkAVdy+JsWLWMUCswAAA",
+ "index": 0,
+ "leafHash": "1249ed899e8c745ce7d2c78925aa51a800b310a5ddd1994c6b7e3f8aad079eb5",
+ "result": "cHNidP8BAF4CAAAAAXbYuDMSrbXuy1CXHZaiA8jjI+wcNkkCOL2dJD2dNf3kAAAAAAD/////AZBBBgAAAAAAIlEgcJi/YJDTvc6x6mRTIYgQG/+fEmLs2RjsO/FHW5okOEsAAAAAAAEBK6BoBgAAAAAAIlEgcJi/YJDTvc6x6mRTIYgQG/+fEmLs2RjsO/FHW5okOEsBCP0HAQNAfYaCk2E35rtXsMfbr/Hrbk/BtLF2VLepE0vIIB3UOWZEUF8akmlnL4Kh0xwMn4Cp99QivnI0B3XOsYfEznfDHSIgbvxAe72lD6NH8HX53P1dC1TSMWCgLkAVdy+JsWLWMUCsocCMbJX6Aw/CG4+BDTnJNS3Z2YTbEkt2R4cFqJqnZ6PZK7Qk3qCfhAuTKgA3PNzb0lZQuMOs/lSp9KZBooZyG40m2seVdmu9oerqpF5b+gqVD91fTEqtpbHzCC7clom5/QruKKO4LErsvr03gSfciN0Z4eqT7bp6J7dU4ciB0wiHSrjxLulfcRJ0q4Qngmj2z4/BEU28Z2iQ4cFJ5+uCkTUkAAA="
+ }
+ },
"finalizeAllInputs": [
{
"type": "P2PK",
@@ -551,6 +895,18 @@
"incorrectPubkey": "Buffer.from('029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e02a', 'hex')",
"nonExistantIndex": 42
},
+ "validateSignaturesOfTapKeyInput": {
+ "psbt": "cHNidP8BAM8CAAAAAwPzd9k+uLSN1rgF01xY1TIA/8N+YytNZ4VP9gKFP4MyAAAAAAD/////ZtAAqL2E1fKcmGo+7xuqS+nSQeKFVKGRYaHfIvLXn4sAAAAAAP////9+h+SlCwIx1MUDT7Bek0NrWXS7xnSPi5LbYbDc9sxYIgAAAAAA/////wIgKRsAAAAAACJRIEb2SXyy8Z1Qw+npgqlQ3MhiFLAfzOQ3pCBhx72xIw0zuAUBAAAAAAAWABTJijE0v48z5ZmmfEAADXdCBcG0FAAAAAAAAQDiAgAAAAABAUfY2D1t0dyMeEH39C1yOdIxigpqm7XJNqHVT3Lc+FkiAAAAAAD+////AhIsGwAAAAAAGXapFJ5+8XZ3ZP80oFldvEwrcNsBftBmiKyYdK6xAAAAABepFLDBn59UffGbX7u/olyFDG0eG1UJhwJHMEQCIDAd3s05C61flXVFqOtov0NoHRGr8KFcOpH6R/81F46EAiBt+j9hHyvT2hYEyf8fdYsM9IgbnybtPV+kRTHDa6Rj0AEhAmmZfwmoHsmCkEOn9AfRTh+863mURelmE8hSqL4MG1EydJwgACICAi5ovBH1xLoGxPqtFh48wUEpnM+St1SbPzRwO7kBNKOQRzBEAiBpWClBybtHveXkhAgTiE8QSczMJs8MGuH4LOSNRA6s/AIgWlbB3xJOtJIsszj1qZ/whA5jK9wnTzeZzDlVs/ivq2cBAAEBK7gFAQAAAAAAIlEgT+9cUWO+ppqT50pZZyu+sIGDcHfLlM+m5IGlzwDYqxgBE0B0eYK4chVhtLT9WMi14T8ZknZSdTe1pMdIvaq6tIfwqY2xQ9YlcTTy0jWU9utItw/rHQ2c1FplbF9bRvZ6RLQSARcglCHnNLD50sRn6n3Rl8Yay0RnzcvJ9MsMVx+LY6XEDK4AAQEfECcAAAAAAAAWABRPoqyhKLb53uUwnE9wBR5Jxl/XMiICA6VOpEBbPJM/xYsqO2euttYFpgec9vcxggyTyoklK660SDBFAiEAoCIktghL55iuMAmkzwYJzb+h+qmNewZXxAx/06ObxIQCIELCsBz/wd2wPlnJb27OluxMkTPnCyHA2C+SxHiX/FvPAQAAAA==",
+ "index": 1,
+ "pubkey": "Buffer.from('024fef5c5163bea69a93e74a59672bbeb081837077cb94cfa6e481a5cf00d8ab18', 'hex')",
+ "incorrectPubkey": "Buffer.from('029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e02a', 'hex')"
+ },
+ "validateSignaturesOfTapScriptInput": {
+ "psbt": "cHNidP8BAF4CAAAAAQU2GLj/HTOEN804Hc6JRNTLM7EmDlIBdjG2G1aUw266AAAAAAD/////AYAaBgAAAAAAIlEg35sLGepBXUbR93XfxDJcYBCHqNVjw/jogOgPVdic1UgAAAAAAAEBK6BoBgAAAAAAIlEgVZx4+tHeORcb0jDJnOytrOnNOGL1uS0MdMUg1GQeS15BFDlfgSndY7SlwvEhJOqgW3p+0w9w5R+5MwXe7MVC5/nrrqI0FdZdKILLLZgRVK8L9Bn2ijU6IcoqqyImKIWt3MtAA3alBoU7IBCkBk9OHD1wE8fJI4y+lbnTRj48e8AAwRM77q3Rml679qCzGvEAKAs99UNMaXHQIhgGfRP11AMlJkEUj4kary4texRheMVKh+Ku3dnR1oZpIleSjmfPCBdP83WuojQV1l0ogsstmBFUrwv0GfaKNTohyiqrIiYoha3cy0DYJZ6Lv7FZPIBRZFfVgF5v3gcRiQnT8aM82Q5IPkwkzZrGo4ThZblvunG/+hu8ZPuJrUU+uXb+s9rcwSH+BihIQRSoqze8FgnYNMOROzU42tHITX+baoNf/BdXd5FaN641cq6iNBXWXSiCyy2YEVSvC/QZ9oo1OiHKKqsiJiiFrdzLQEfQ5UkAg4lTbhJxjMzB7hu6ad1fywYxHCXjFXHHrm5PJTOFJLg2oTnwuQToz/Z2AW/UET7Op+WSoHZvW4tzzLhiFcFmuQP4s1ds7KJtMOh4fTw1QCgxkWUA3FUAuUzKHzjDvhpSnJ+zzX53bWG2IltsYQ6JBvuPqmxZrFw+lbX4LSnWGlKcn7PNfndtYbYiW2xhDokG+4+qbFmsXD6VtfgtKdZpII+JGq8uLXsUYXjFSofirt3Z0daGaSJXko5nzwgXT/N1rCA5X4Ep3WO0pcLxISTqoFt6ftMPcOUfuTMF3uzFQuf567ogqKs3vBYJ2DTDkTs1ONrRyE1/m2qDX/wXV3eRWjeuNXK6U5zAAAA=",
+ "index": 0,
+ "pubkey": "Buffer.from('02395f8129dd63b4a5c2f12124eaa05b7a7ed30f70e51fb93305deecc542e7f9eb', 'hex')",
+ "incorrectPubkey": "Buffer.from('029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e02a', 'hex')"
+ },
"getFeeRate": {
"psbt": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAAiAgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgf0cwRAIgdAGK1BgAl7hzMjwAFXILNoTMgSOJEEjn282bVa1nnJkCIHPTabdA4+tT3O+jOCPIBwUUylWn3ZVE8VfBZ5EyYRGMASICAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXSDBFAiEA9hA4swjcHahlo0hSdG8BV3KTQgjG0kRUOTzZm98iF3cCIAVuZ1pnWm0KArhbFOXikHTYolqbV2C+ooFvZhkQoAbqAQEDBAEAAAABBEdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSriIGApWDvzmuCmCXR60Zmt3WNPphCFWdbFzTm0whg/GrluB/ENkMak8AAACAAAAAgAAAAIAiBgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU21xDZDGpPAAAAgAAAAIABAACAAAEBIADC6wsAAAAAF6kUt/X69A49QKWkWbHbNTXyty+pIeiHIgIDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtxHMEQCIGLrelVhB6fHP0WsSrWh3d9vcHX7EnWWmn84Pv/3hLyyAiAMBdu3Rw2/LwhVfdNWxzJcHtMJE+mWzThAlF2xIijaXwEiAgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc0cwRAIgZfRbpZmLWaJ//hp77QFq8fH5DVSzqo90UKpfVqJRA70CIH9yRwOtHtuWaAsoS1bU/8uI9/t1nqu+CKow8puFE4PSAQEDBAEAAAABBCIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAQVHUiEDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwhAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zUq4iBgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8OcxDZDGpPAAAAgAAAAIADAACAIgYDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwQ2QxqTwAAAIAAAACAAgAAgAAiAgOppMN/WZbTqiXbrGtXCvBlA5RJKUJGCzVHU+2e7KWHcRDZDGpPAAAAgAAAAIAEAACAACICAn9jmXV9Lv9VoTatAsaEsYOLZVbl8bazQoKpS2tQBRCWENkMak8AAACAAAAAgAUAAIAA",
"fee": 21
diff --git a/test/fixtures/script.json b/test/fixtures/script.json
index 545c8f3bb..d435cea91 100644
--- a/test/fixtures/script.json
+++ b/test/fixtures/script.json
@@ -457,7 +457,7 @@
],
"fromASM": [
{
- "description": "Expected Hex, got String \"0xff\"",
+ "description": "ValiError: Invalid format: Expected \\\/\\^\\(\\[0-9a-f\\]\\{2\\}\\)\\+\\$\\\/i but received \"0xff\"",
"script": "0xff OP_CHECKSIG"
}
]
diff --git a/test/fixtures/transaction.json b/test/fixtures/transaction.json
index 979549ab7..c5fae8cc0 100644
--- a/test/fixtures/transaction.json
+++ b/test/fixtures/transaction.json
@@ -693,6 +693,36 @@
"coinbase": true,
"virtualSize": 156,
"weight": 624
+ },
+ {
+ "description": "Litecoin MWEB peg-out transaction",
+ "id": "efe11e0d8d562e73b7795c2a3b7e44c6b6390f2c42c3ae90bb1005009c27a3f3",
+ "hash": "f3a3279c000510bb90aec3422c0f39b6c6447e3b2a5c79b7732e568d0d1ee1ef",
+ "hex": "02000000000801f190b67fc759641b8376f5ca550cac80ca3b2509c6a144e1425a437b18c161590000000000ffffffff020675a5b66a00000022582066723521c495f90a5fbe3686c617c294d69ac71ed9e57b65032f83e45871fd83b28c980000000000160014f4de962f4bb82d0057974201202acd78d56db7f20000000000",
+ "raw": {
+ "version": 2,
+ "ins": [
+ {
+ "hash": "f190b67fc759641b8376f5ca550cac80ca3b2509c6a144e1425a437b18c16159",
+ "index": 0,
+ "data": ""
+ }
+ ],
+ "outs": [
+ {
+ "data": "582066723521c495f90a5fbe3686c617c294d69ac71ed9e57b65032f83e45871fd83",
+ "value": 458330830086
+ },
+ {
+ "data": "0014f4de962f4bb82d0057974201202acd78d56db7f2",
+ "value": 9997490
+ }
+ ],
+ "locktime": 0
+ },
+ "coinbase": false,
+ "virtualSize": 125,
+ "weight": 500
}
],
"hashForSignature": [
@@ -892,12 +922,12 @@
"invalid": {
"addInput": [
{
- "exception": "Expected property \"0\" of type Buffer\\(Length: 32\\), got Buffer\\(Length: 30\\)",
+ "exception": "ValiError: Invalid length: Expected 32 but received 30",
"hash": "0aed1366a73b6057ee7800d737bff1bdf8c448e98d86bc0998f2b009816d",
"index": 0
},
{
- "exception": "Expected property \"0\" of type Buffer\\(Length: 32\\), got Buffer\\(Length: 34\\)",
+ "exception": "ValiError: Invalid length: Expected 32 but received 34",
"hash": "0aed1366a73b6057ee7800d737bff1bdf8c448e98d86bc0998f2b009816da9b0ffff",
"index": 0
}
diff --git a/test/integration/addresses.spec.ts b/test/integration/addresses.spec.ts
index 2b24ef5a0..0b234c473 100644
--- a/test/integration/addresses.spec.ts
+++ b/test/integration/addresses.spec.ts
@@ -1,17 +1,23 @@
import * as assert from 'assert';
-import { ECPair } from 'ecpair';
+import ECPairFactory from 'ecpair';
+import * as ecc from 'tiny-secp256k1';
import { describe, it } from 'mocha';
-import * as bitcoin from '../..';
-import { regtestUtils } from './_regtest';
+import * as bitcoin from 'bitcoinjs-lib';
+import { regtestUtils } from './_regtest.js';
+import { randomBytes } from 'crypto';
+
+const ECPair = ECPairFactory(ecc);
const dhttp = regtestUtils.dhttp;
const TESTNET = bitcoin.networks.testnet;
+const rng = (size: number) => randomBytes(size);
+
describe('bitcoinjs-lib (addresses)', () => {
it(
'can generate a random address [and support the retrieval of ' +
'transactions for that address (via 3PBP)]',
async () => {
- const keyPair = ECPair.makeRandom();
+ const keyPair = ECPair.makeRandom({ rng });
const { address } = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey });
// bitcoin P2PKH addresses start with a '1'
@@ -104,7 +110,7 @@ describe('bitcoinjs-lib (addresses)', () => {
// examples using other network information
it('can generate a Testnet address', () => {
- const keyPair = ECPair.makeRandom({ network: TESTNET });
+ const keyPair = ECPair.makeRandom({ network: TESTNET, rng });
const { address } = bitcoin.payments.p2pkh({
pubkey: keyPair.publicKey,
network: TESTNET,
@@ -131,7 +137,7 @@ describe('bitcoinjs-lib (addresses)', () => {
wif: 0xb0,
};
- const keyPair = ECPair.makeRandom({ network: LITECOIN });
+ const keyPair = ECPair.makeRandom({ network: LITECOIN, rng });
const { address } = bitcoin.payments.p2pkh({
pubkey: keyPair.publicKey,
network: LITECOIN,
diff --git a/test/integration/bip32.spec.ts b/test/integration/bip32.spec.ts
index 7cd9e2f55..ac1088b7e 100644
--- a/test/integration/bip32.spec.ts
+++ b/test/integration/bip32.spec.ts
@@ -1,8 +1,11 @@
import * as assert from 'assert';
-import * as bip32 from 'bip32';
+import BIP32Factory from 'bip32';
+import * as ecc from 'tiny-secp256k1';
import * as bip39 from 'bip39';
import { describe, it } from 'mocha';
-import * as bitcoin from '../..';
+import * as bitcoin from 'bitcoinjs-lib';
+
+const bip32 = BIP32Factory(ecc);
function getAddress(node: any, network?: any): string {
return bitcoin.payments.p2pkh({ pubkey: node.publicKey, network }).address!;
@@ -57,10 +60,7 @@ describe('bitcoinjs-lib (BIP32)', () => {
const child1 = root.derivePath(path);
// option 2, manually
- const child1b = root
- .deriveHardened(0)
- .derive(0)
- .derive(0);
+ const child1b = root.deriveHardened(0).derive(0).derive(0);
assert.strictEqual(
getAddress(child1),
@@ -123,7 +123,7 @@ describe('bitcoinjs-lib (BIP32)', () => {
// var mnemonic = bip39.generateMnemonic()
const mnemonic =
'praise you muffin lion enable neck grocery crumble super myself license ghost';
- assert(bip39.validateMnemonic(mnemonic));
+ assert.strictEqual(bip39.validateMnemonic(mnemonic), true);
const seed = bip39.mnemonicToSeedSync(mnemonic);
const root = bip32.fromSeed(seed);
diff --git a/test/integration/blocks.spec.ts b/test/integration/blocks.spec.ts
index a98c5eb79..e1533f955 100644
--- a/test/integration/blocks.spec.ts
+++ b/test/integration/blocks.spec.ts
@@ -1,6 +1,6 @@
import * as assert from 'assert';
import { describe, it } from 'mocha';
-import * as bitcoin from '../..';
+import * as bitcoin from 'bitcoinjs-lib';
describe('bitcoinjs-lib (blocks)', () => {
it('can extract a height from a CoinBase transaction', () => {
diff --git a/test/integration/cltv.spec.ts b/test/integration/cltv.spec.ts
index c1a52de76..0e532fc5b 100644
--- a/test/integration/cltv.spec.ts
+++ b/test/integration/cltv.spec.ts
@@ -1,17 +1,22 @@
import * as assert from 'assert';
-import { ECPair } from 'ecpair';
+import ECPairFactory from 'ecpair';
+import * as ecc from 'tiny-secp256k1';
import { before, describe, it } from 'mocha';
-import * as bitcoin from '../..';
-import { regtestUtils } from './_regtest';
+import * as bitcoin from 'bitcoinjs-lib';
+import { regtestUtils } from './_regtest.js';
+import { reverseBuffer } from 'bitcoinjs-lib/src/bufferutils';
+import * as tools from 'uint8array-tools';
+
+const ECPair = ECPairFactory(ecc);
const regtest = regtestUtils.network;
-const bip65 = require('bip65');
+import bip65 from 'bip65';
-function toOutputScript(address: string): Buffer {
+function toOutputScript(address: string): Uint8Array {
return bitcoin.address.toOutputScript(address, regtest);
}
-function idToHash(txid: string): Buffer {
- return Buffer.from(txid, 'hex').reverse();
+function idToHash(txid: string): Uint8Array {
+ return reverseBuffer(tools.fromHex(txid));
}
const alice = ECPair.fromWIF(
@@ -32,24 +37,24 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', () => {
const hashType = bitcoin.Transaction.SIGHASH_ALL;
interface KeyPair {
- publicKey: Buffer;
+ publicKey: Uint8Array;
}
function cltvCheckSigOutput(
aQ: KeyPair,
bQ: KeyPair,
lockTime: number,
- ): Buffer {
+ ): Uint8Array {
return bitcoin.script.fromASM(
`
OP_IF
- ${bitcoin.script.number.encode(lockTime).toString('hex')}
+ ${tools.toHex(bitcoin.script.number.encode(lockTime))}
OP_CHECKLOCKTIMEVERIFY
OP_DROP
OP_ELSE
- ${bQ.publicKey.toString('hex')}
+ ${tools.toHex(bQ.publicKey)}
OP_CHECKSIGVERIFY
OP_ENDIF
- ${aQ.publicKey.toString('hex')}
+ ${tools.toHex(aQ.publicKey)}
OP_CHECKSIG
`
.trim()
@@ -78,9 +83,9 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', () => {
const unspent = await regtestUtils.faucet(address!, 1e5);
const tx = new bitcoin.Transaction();
tx.locktime = lockTime;
- // Note: nSequence MUST be <= 0xfffffffe otherwise LockTime is ignored, and is immediately spendable.
+ // Note: nSequence MUST be <= 0xfffffffe otherwise OP_CHECKLOCKTIMEVERIFY will fail.
tx.addInput(idToHash(unspent.txId), unspent.vout, 0xfffffffe);
- tx.addOutput(toOutputScript(regtestUtils.RANDOM_ADDRESS), 7e4);
+ tx.addOutput(toOutputScript(regtestUtils.RANDOM_ADDRESS), BigInt(7e4));
// {Alice's signature} OP_TRUE
const signatureHash = tx.hashForSignature(0, redeemScript, hashType);
@@ -127,9 +132,9 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', () => {
const unspent = await regtestUtils.faucet(address!, 1e5);
const tx = new bitcoin.Transaction();
tx.locktime = lockTime;
- // Note: nSequence MUST be <= 0xfffffffe otherwise LockTime is ignored, and is immediately spendable.
+ // Note: nSequence MUST be <= 0xfffffffe otherwise OP_CHECKLOCKTIMEVERIFY will fail.
tx.addInput(idToHash(unspent.txId), unspent.vout, 0xfffffffe);
- tx.addOutput(toOutputScript(regtestUtils.RANDOM_ADDRESS), 7e4);
+ tx.addOutput(toOutputScript(regtestUtils.RANDOM_ADDRESS), BigInt(7e4));
// {Alice's signature} OP_TRUE
const signatureHash = tx.hashForSignature(0, redeemScript, hashType);
@@ -178,9 +183,9 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', () => {
const unspent = await regtestUtils.faucet(address!, 2e5);
const tx = new bitcoin.Transaction();
tx.locktime = lockTime;
- // Note: nSequence MUST be <= 0xfffffffe otherwise LockTime is ignored, and is immediately spendable.
+ // Note: nSequence MUST be <= 0xfffffffe otherwise OP_CHECKLOCKTIMEVERIFY will fail.
tx.addInput(idToHash(unspent.txId), unspent.vout, 0xfffffffe);
- tx.addOutput(toOutputScript(regtestUtils.RANDOM_ADDRESS), 8e4);
+ tx.addOutput(toOutputScript(regtestUtils.RANDOM_ADDRESS), BigInt(8e4));
// {Alice's signature} {Bob's signature} OP_FALSE
const signatureHash = tx.hashForSignature(0, redeemScript, hashType);
@@ -226,9 +231,9 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', () => {
const unspent = await regtestUtils.faucet(address!, 2e4);
const tx = new bitcoin.Transaction();
tx.locktime = lockTime;
- // Note: nSequence MUST be <= 0xfffffffe otherwise LockTime is ignored, and is immediately spendable.
+ // Note: nSequence MUST be <= 0xfffffffe otherwise OP_CHECKLOCKTIMEVERIFY will fail.
tx.addInput(idToHash(unspent.txId), unspent.vout, 0xfffffffe);
- tx.addOutput(toOutputScript(regtestUtils.RANDOM_ADDRESS), 1e4);
+ tx.addOutput(toOutputScript(regtestUtils.RANDOM_ADDRESS), BigInt(1e4));
// {Alice's signature} OP_TRUE
const signatureHash = tx.hashForSignature(0, redeemScript, hashType);
diff --git a/test/integration/csv.spec.ts b/test/integration/csv.spec.ts
index 9993d5c30..6e8754e3a 100644
--- a/test/integration/csv.spec.ts
+++ b/test/integration/csv.spec.ts
@@ -1,19 +1,24 @@
import * as assert from 'assert';
-import { PsbtInput } from 'bip174/src/lib/interfaces';
-import { ECPair } from 'ecpair';
+import { PsbtInput } from 'bip174';
+import ECPairFactory from 'ecpair';
+import * as ecc from 'tiny-secp256k1';
import { before, describe, it } from 'mocha';
-import * as bitcoin from '../..';
-import { regtestUtils } from './_regtest';
+import * as bitcoin from 'bitcoinjs-lib';
+import { regtestUtils } from './_regtest.js';
+import { reverseBuffer } from 'bitcoinjs-lib/src/bufferutils';
+import * as tools from 'uint8array-tools';
+
+const ECPair = ECPairFactory(ecc);
const regtest = regtestUtils.network;
-const bip68 = require('bip68');
-const varuint = require('varuint-bitcoin');
+import bip68 from 'bip68';
+import * as varuint from 'varuint-bitcoin';
-function toOutputScript(address: string): Buffer {
+function toOutputScript(address: string): Uint8Array {
return bitcoin.address.toOutputScript(address, regtest);
}
-function idToHash(txid: string): Buffer {
- return Buffer.from(txid, 'hex').reverse();
+function idToHash(txid: string): Uint8Array {
+ return reverseBuffer(tools.fromHex(txid));
}
const alice = ECPair.fromWIF(
@@ -42,25 +47,25 @@ describe('bitcoinjs-lib (transactions w/ CSV)', () => {
const hashType = bitcoin.Transaction.SIGHASH_ALL;
interface KeyPair {
- publicKey: Buffer;
+ publicKey: Uint8Array;
}
// IF MTP (from when confirmed) > seconds, _alice can redeem
function csvCheckSigOutput(
_alice: KeyPair,
_bob: KeyPair,
sequence: number,
- ): Buffer {
+ ): Uint8Array {
return bitcoin.script.fromASM(
`
OP_IF
- ${bitcoin.script.number.encode(sequence).toString('hex')}
+ ${tools.toHex(bitcoin.script.number.encode(sequence))}
OP_CHECKSEQUENCEVERIFY
OP_DROP
OP_ELSE
- ${_bob.publicKey.toString('hex')}
+ ${tools.toHex(_bob.publicKey)}
OP_CHECKSIGVERIFY
OP_ENDIF
- ${_alice.publicKey.toString('hex')}
+ ${tools.toHex(_alice.publicKey)}
OP_CHECKSIG
`
.trim()
@@ -72,7 +77,6 @@ describe('bitcoinjs-lib (transactions w/ CSV)', () => {
// but after sequence1 time, _alice can allow the multisig to become 1 of 3.
// but after sequence2 time, _alice can sign for the output all by themself.
- /* tslint:disable-next-line */
// Ref: https://github.com/bitcoinbook/bitcoinbook/blob/f8b883dcd4e3d1b9adf40fed59b7e898fbd9241f/ch07.asciidoc#complex-script-example
// Note: bitcoinjs-lib will not offer specific support for problems with
@@ -84,30 +88,30 @@ describe('bitcoinjs-lib (transactions w/ CSV)', () => {
_dave: KeyPair,
sequence1: number,
sequence2: number,
- ): Buffer {
+ ): Uint8Array {
return bitcoin.script.fromASM(
`
OP_IF
OP_IF
OP_2
OP_ELSE
- ${bitcoin.script.number.encode(sequence1).toString('hex')}
+ ${tools.toHex(bitcoin.script.number.encode(sequence1))}
OP_CHECKSEQUENCEVERIFY
OP_DROP
- ${_alice.publicKey.toString('hex')}
+ ${tools.toHex(_alice.publicKey)}
OP_CHECKSIGVERIFY
OP_1
OP_ENDIF
- ${_bob.publicKey.toString('hex')}
- ${_charles.publicKey.toString('hex')}
- ${_dave.publicKey.toString('hex')}
+ ${tools.toHex(_bob.publicKey)}
+ ${tools.toHex(_charles.publicKey)}
+ ${tools.toHex(_dave.publicKey)}
OP_3
OP_CHECKMULTISIG
OP_ELSE
- ${bitcoin.script.number.encode(sequence2).toString('hex')}
+ ${tools.toHex(bitcoin.script.number.encode(sequence2))}
OP_CHECKSEQUENCEVERIFY
OP_DROP
- ${_alice.publicKey.toString('hex')}
+ ${tools.toHex(_alice.publicKey)}
OP_CHECKSIG
OP_ENDIF
`
@@ -149,7 +153,7 @@ describe('bitcoinjs-lib (transactions w/ CSV)', () => {
})
.addOutput({
address: regtestUtils.RANDOM_ADDRESS,
- value: 7e4,
+ value: BigInt(7e4),
})
.signInput(0, alice)
.finalizeInput(0, csvGetFinalScripts) // See csvGetFinalScripts below
@@ -169,7 +173,7 @@ describe('bitcoinjs-lib (transactions w/ CSV)', () => {
value: 7e4,
});
},
- );
+ ).timeout(8000);
// expiry in the future, {Alice's signature} OP_TRUE
it(
@@ -191,7 +195,7 @@ describe('bitcoinjs-lib (transactions w/ CSV)', () => {
const tx = new bitcoin.Transaction();
tx.version = 2;
tx.addInput(idToHash(unspent.txId), unspent.vout, sequence);
- tx.addOutput(toOutputScript(regtestUtils.RANDOM_ADDRESS), 1e4);
+ tx.addOutput(toOutputScript(regtestUtils.RANDOM_ADDRESS), BigInt(1e4));
// {Alice's signature} OP_TRUE
const signatureHash = tx.hashForSignature(
@@ -222,7 +226,7 @@ describe('bitcoinjs-lib (transactions w/ CSV)', () => {
}, /Error: non-BIP68-final/);
});
},
- );
+ ).timeout(8000);
// Check first combination of complex CSV, 2 of 3
it(
@@ -253,7 +257,7 @@ describe('bitcoinjs-lib (transactions w/ CSV)', () => {
const tx = new bitcoin.Transaction();
tx.version = 2;
tx.addInput(idToHash(unspent.txId), unspent.vout);
- tx.addOutput(toOutputScript(regtestUtils.RANDOM_ADDRESS), 7e4);
+ tx.addOutput(toOutputScript(regtestUtils.RANDOM_ADDRESS), BigInt(7e4));
// OP_0 {Bob sig} {Charles sig} OP_TRUE OP_TRUE
const signatureHash = tx.hashForSignature(
@@ -289,7 +293,7 @@ describe('bitcoinjs-lib (transactions w/ CSV)', () => {
value: 7e4,
});
},
- );
+ ).timeout(8000);
// Check first combination of complex CSV, mediator + 1 of 3 after 2 blocks
it(
@@ -320,7 +324,7 @@ describe('bitcoinjs-lib (transactions w/ CSV)', () => {
const tx = new bitcoin.Transaction();
tx.version = 2;
tx.addInput(idToHash(unspent.txId), unspent.vout, sequence1); // Set sequence1 for input
- tx.addOutput(toOutputScript(regtestUtils.RANDOM_ADDRESS), 7e4);
+ tx.addOutput(toOutputScript(regtestUtils.RANDOM_ADDRESS), BigInt(7e4));
// OP_0 {Bob sig} {Alice mediator sig} OP_FALSE OP_TRUE
const signatureHash = tx.hashForSignature(
@@ -359,7 +363,7 @@ describe('bitcoinjs-lib (transactions w/ CSV)', () => {
value: 7e4,
});
},
- );
+ ).timeout(8000);
// Check first combination of complex CSV, mediator after 5 blocks
it(
@@ -390,7 +394,7 @@ describe('bitcoinjs-lib (transactions w/ CSV)', () => {
const tx = new bitcoin.Transaction();
tx.version = 2;
tx.addInput(idToHash(unspent.txId), unspent.vout, sequence2); // Set sequence2 for input
- tx.addOutput(toOutputScript(regtestUtils.RANDOM_ADDRESS), 7e4);
+ tx.addOutput(toOutputScript(regtestUtils.RANDOM_ADDRESS), BigInt(7e4));
// {Alice mediator sig} OP_FALSE
const signatureHash = tx.hashForSignature(
@@ -426,7 +430,7 @@ describe('bitcoinjs-lib (transactions w/ CSV)', () => {
value: 7e4,
});
},
- );
+ ).timeout(8000);
});
// This function is used to finalize a CSV transaction using PSBT.
@@ -434,13 +438,13 @@ describe('bitcoinjs-lib (transactions w/ CSV)', () => {
function csvGetFinalScripts(
inputIndex: number,
input: PsbtInput,
- script: Buffer,
+ script: Uint8Array,
isSegwit: boolean,
isP2SH: boolean,
isP2WSH: boolean,
): {
- finalScriptSig: Buffer | undefined;
- finalScriptWitness: Buffer | undefined;
+ finalScriptSig: Uint8Array | undefined;
+ finalScriptWitness: Uint8Array | undefined;
} {
// Step 1: Check to make sure the meaningful script matches what you expect.
const decompiled = bitcoin.script.decompile(script);
@@ -475,11 +479,11 @@ function csvGetFinalScripts(
redeem: payment,
});
- function witnessStackToScriptWitness(witness: Buffer[]): Buffer {
- let buffer = Buffer.allocUnsafe(0);
+ function witnessStackToScriptWitness(witness: Uint8Array[]): Uint8Array {
+ let buffer = new Uint8Array(0);
- function writeSlice(slice: Buffer): void {
- buffer = Buffer.concat([buffer, Buffer.from(slice)]);
+ function writeSlice(slice: Uint8Array): void {
+ buffer = Buffer.concat([buffer, slice]);
}
function writeVarInt(i: number): void {
@@ -490,12 +494,12 @@ function csvGetFinalScripts(
varuint.encode(i, buffer, currentLen);
}
- function writeVarSlice(slice: Buffer): void {
+ function writeVarSlice(slice: Uint8Array): void {
writeVarInt(slice.length);
writeSlice(slice);
}
- function writeVector(vector: Buffer[]): void {
+ function writeVector(vector: Uint8Array[]): void {
writeVarInt(vector.length);
vector.forEach(writeVarSlice);
}
diff --git a/test/integration/payments.spec.ts b/test/integration/payments.spec.ts
index ea7294ec2..c0b2f4a24 100644
--- a/test/integration/payments.spec.ts
+++ b/test/integration/payments.spec.ts
@@ -1,11 +1,41 @@
-import { ECPair } from 'ecpair';
+import ECPairFactory from 'ecpair';
+import * as ecc from 'tiny-secp256k1';
import { describe, it } from 'mocha';
-import * as bitcoin from '../..';
-import { regtestUtils } from './_regtest';
+import * as bitcoin from 'bitcoinjs-lib';
+import { regtestUtils } from './_regtest.js';
+import { randomBytes } from 'crypto';
+
+import p2msFixtures from '../fixtures/p2ms.json';
+import p2pkFixtures from '../fixtures/p2pk.json';
+import p2pkhFixtures from '../fixtures/p2pkh.json';
+import p2wpkhFixtures from '../fixtures/p2wpkh.json';
+
+const rng = (size: number) => randomBytes(size);
+
+const testSuite = [
+ {
+ paymentName: 'p2ms',
+ fixtures: p2msFixtures,
+ },
+ {
+ paymentName: 'p2pk',
+ fixtures: p2pkFixtures,
+ },
+ {
+ paymentName: 'p2pkh',
+ fixtures: p2pkhFixtures,
+ },
+ {
+ paymentName: 'p2wpkh',
+ fixtures: p2wpkhFixtures,
+ },
+];
+
+const ECPair = ECPairFactory(ecc);
const NETWORK = regtestUtils.network;
const keyPairs = [
- ECPair.makeRandom({ network: NETWORK }),
- ECPair.makeRandom({ network: NETWORK }),
+ ECPair.makeRandom({ network: NETWORK, rng }),
+ ECPair.makeRandom({ network: NETWORK, rng }),
];
async function buildAndSign(
@@ -14,7 +44,10 @@ async function buildAndSign(
redeemScript: any,
witnessScript: any,
): Promise {
- const unspent = await regtestUtils.faucetComplex(prevOutput, 5e4);
+ const unspent = await regtestUtils.faucetComplex(
+ Buffer.from(prevOutput),
+ 5e4,
+ );
const utx = await regtestUtils.fetch(unspent.txId);
const psbt = new bitcoin.Psbt({ network: NETWORK })
@@ -27,7 +60,7 @@ async function buildAndSign(
})
.addOutput({
address: regtestUtils.RANDOM_ADDRESS,
- value: 2e4,
+ value: BigInt(2e4),
});
if (depends.signatures) {
@@ -39,17 +72,14 @@ async function buildAndSign(
}
return regtestUtils.broadcast(
- psbt
- .finalizeAllInputs()
- .extractTransaction()
- .toHex(),
+ psbt.finalizeAllInputs().extractTransaction().toHex(),
);
}
-['p2ms', 'p2pk', 'p2pkh', 'p2wpkh'].forEach(k => {
- const fixtures = require('../fixtures/' + k);
+testSuite.forEach(t => {
+ const fixtures = t.fixtures;
const { depends } = fixtures.dynamic;
- const fn: any = (bitcoin.payments as any)[k];
+ const fn: any = (bitcoin.payments as any)[t.paymentName];
const base: any = {};
if (depends.pubkey) base.pubkey = keyPairs[0].publicKey;
@@ -59,22 +89,22 @@ async function buildAndSign(
const { output } = fn(base);
if (!output) throw new TypeError('Missing output');
- describe('bitcoinjs-lib (payments - ' + k + ')', () => {
+ describe('bitcoinjs-lib (payments - ' + t.paymentName + ')', () => {
it('can broadcast as an output, and be spent as an input', async () => {
- Object.assign(depends, { prevOutScriptType: k });
+ Object.assign(depends, { prevOutScriptType: t.paymentName });
await buildAndSign(depends, output, undefined, undefined);
});
it(
'can (as P2SH(' +
- k +
+ t.paymentName +
')) broadcast as an output, and be spent as an input',
async () => {
const p2sh = bitcoin.payments.p2sh({
redeem: { output },
network: NETWORK,
});
- Object.assign(depends, { prevOutScriptType: 'p2sh-' + k });
+ Object.assign(depends, { prevOutScriptType: 'p2sh-' + t.paymentName });
await buildAndSign(
depends,
p2sh.output,
@@ -85,18 +115,18 @@ async function buildAndSign(
);
// NOTE: P2WPKH cannot be wrapped in P2WSH, consensus fail
- if (k === 'p2wpkh') return;
+ if (t.paymentName === 'p2wpkh') return;
it(
'can (as P2WSH(' +
- k +
+ t.paymentName +
')) broadcast as an output, and be spent as an input',
async () => {
const p2wsh = bitcoin.payments.p2wsh({
redeem: { output },
network: NETWORK,
});
- Object.assign(depends, { prevOutScriptType: 'p2wsh-' + k });
+ Object.assign(depends, { prevOutScriptType: 'p2wsh-' + t.paymentName });
await buildAndSign(
depends,
p2wsh.output,
@@ -108,7 +138,7 @@ async function buildAndSign(
it(
'can (as P2SH(P2WSH(' +
- k +
+ t.paymentName +
'))) broadcast as an output, and be spent as an input',
async () => {
const p2wsh = bitcoin.payments.p2wsh({
@@ -120,7 +150,9 @@ async function buildAndSign(
network: NETWORK,
});
- Object.assign(depends, { prevOutScriptType: 'p2sh-p2wsh-' + k });
+ Object.assign(depends, {
+ prevOutScriptType: 'p2sh-p2wsh-' + t.paymentName,
+ });
await buildAndSign(
depends,
p2sh.output,
diff --git a/test/integration/taproot.md b/test/integration/taproot.md
deleted file mode 100644
index 401034061..000000000
--- a/test/integration/taproot.md
+++ /dev/null
@@ -1,156 +0,0 @@
-# Taproot
-
-A simple keyspend example that is possible with the current API is below.
-
-## Current state of taproot support
-
-- [x] segwit v1 address support via bech32m
-- [x] segwit v1 sighash calculation on Transaction class
-
-## TODO
-
-- [ ] p2tr payment API to make script spends easier
-- [ ] Support within the Psbt class
-
-## Example
-
-### Requirements
-- npm dependencies
- - bitcoinjs-lib v6.x.x
- - bip32 v3.x.x
- - tiny-secp256k1 v2.x.x
- - regtest-client vx.x.x
-- local regtest-server docker container running
- - `docker run -d -p 8080:8080 junderw/bitcoinjs-regtest-server`
-- node >= v14
-
-```js
-// Run this whole file as async
-// Catch any errors at the bottom of the file
-// and exit the process with 1 error code
-(async () => {
-
-// Order of the curve (N) - 1
-const N_LESS_1 = Buffer.from(
- 'fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140',
- 'hex'
-);
-// 1 represented as 32 bytes BE
-const ONE = Buffer.from(
- '0000000000000000000000000000000000000000000000000000000000000001',
- 'hex'
-);
-
-const crypto = require('crypto');
-// bitcoinjs-lib v6
-const bitcoin = require('bitcoinjs-lib');
-// bip32 v3 wraps tiny-secp256k1
-const BIP32Wrapper = require('bip32').default;
-const RegtestUtils = require('regtest-client').RegtestUtils;
-// tiny-secp256k1 v2 is an ESM module, so we can't "require", and must import async
-const ecc = await import('tiny-secp256k1');
-// wrap the bip32 library
-const bip32 = BIP32Wrapper(ecc);
-// set up dependencies
-const APIPASS = process.env.APIPASS || 'satoshi';
-// docker run -d -p 8080:8080 junderw/bitcoinjs-regtest-server
-const APIURL = process.env.APIURL || 'http://127.0.0.1:8080/1';
-const regtestUtils = new RegtestUtils({ APIPASS, APIURL });
-// End imports
-
-const myKey = bip32.fromSeed(crypto.randomBytes(64), regtestUtils.network);
-
-const output = createKeySpendOutput(myKey.publicKey);
-const address = bitcoin.address.fromOutputScript(
- output,
- regtestUtils.network
-);
-// amount from faucet
-const amount = 42e4;
-// amount to send
-const sendAmount = amount - 1e4;
-// get faucet
-const unspent = await regtestUtils.faucetComplex(output, amount);
-
-const tx = createSigned(
- myKey,
- unspent.txId,
- unspent.vout,
- sendAmount,
- [output],
- [amount]
-);
-
-const hex = tx.toHex();
-console.log('Valid tx sent from:');
-console.log(address);
-console.log('tx hex:');
-console.log(hex);
-await regtestUtils.broadcast(hex);
-await regtestUtils.verify({
- txId: tx.getId(),
- address,
- vout: 0,
- value: sendAmount,
-});
-
-// Function for creating a tweaked p2tr key-spend only address
-// (This is recommended by BIP341)
-function createKeySpendOutput(publicKey) {
- // x-only pubkey (remove 1 byte y parity)
- const myXOnlyPubkey = publicKey.slice(1, 33);
- const commitHash = bitcoin.crypto.taggedHash('TapTweak', myXOnlyPubkey);
- const tweakResult = ecc.xOnlyPointAddTweak(myXOnlyPubkey, commitHash);
- if (tweakResult === null) throw new Error('Invalid Tweak');
- const { xOnlyPubkey: tweaked } = tweakResult;
- // scriptPubkey
- return Buffer.concat([
- // witness v1, PUSH_DATA 32 bytes
- Buffer.from([0x51, 0x20]),
- // x-only tweaked pubkey
- tweaked,
- ]);
-}
-
-// Function for signing for a tweaked p2tr key-spend only address
-// (Required for the above address)
-function signTweaked(messageHash, key) {
- const privateKey =
- key.publicKey[0] === 2
- ? key.privateKey
- : ecc.privateAdd(ecc.privateSub(N_LESS_1, key.privateKey), ONE);
- const tweakHash = bitcoin.crypto.taggedHash(
- 'TapTweak',
- key.publicKey.slice(1, 33)
- );
- const newPrivateKey = ecc.privateAdd(privateKey, tweakHash);
- if (newPrivateKey === null) throw new Error('Invalid Tweak');
- return ecc.signSchnorr(messageHash, newPrivateKey, Buffer.alloc(32));
-}
-
-// Function for creating signed tx
-function createSigned(key, txid, vout, amountToSend, scriptPubkeys, values) {
- const tx = new bitcoin.Transaction();
- tx.version = 2;
- // Add input
- tx.addInput(Buffer.from(txid, 'hex').reverse(), vout);
- // Add output
- tx.addOutput(scriptPubkeys[0], amountToSend);
- const sighash = tx.hashForWitnessV1(
- 0, // which input
- scriptPubkeys, // All previous outputs of all inputs
- values, // All previous values of all inputs
- bitcoin.Transaction.SIGHASH_DEFAULT // sighash flag, DEFAULT is schnorr-only (DEFAULT == ALL)
- );
- const signature = Buffer.from(signTweaked(sighash, key));
- // witness stack for keypath spend is just the signature.
- // If sighash is not SIGHASH_DEFAULT (ALL) then you must add 1 byte with sighash value
- tx.ins[0].witness = [signature];
- return tx;
-}
-
-})().catch((err) => {
- console.error(err);
- process.exit(1);
-});
-```
\ No newline at end of file
diff --git a/test/integration/taproot.spec.ts b/test/integration/taproot.spec.ts
new file mode 100644
index 000000000..7f9821a88
--- /dev/null
+++ b/test/integration/taproot.spec.ts
@@ -0,0 +1,1076 @@
+import * as assert from 'assert';
+import BIP32Factory, { BIP32Interface } from 'bip32';
+import * as bip39 from 'bip39';
+import * as ecc from 'tiny-secp256k1';
+import { describe, it } from 'mocha';
+import { PsbtInput, TapLeaf, TapLeafScript } from 'bip174';
+import { regtestUtils } from './_regtest.js';
+import * as bitcoin from 'bitcoinjs-lib';
+import { Taptree } from 'bitcoinjs-lib/src/types';
+import {
+ LEAF_VERSION_TAPSCRIPT,
+ tapleafHash,
+} from 'bitcoinjs-lib/src/payments/bip341';
+import {
+ toXOnly,
+ tapTreeToList,
+ tapTreeFromList,
+} from 'bitcoinjs-lib/src/psbt/bip371';
+import { witnessStackToScriptWitness } from 'bitcoinjs-lib/src/psbt/psbtutils';
+import * as tools from 'uint8array-tools';
+import { sha256 } from '@noble/hashes/sha256';
+import { randomBytes } from 'crypto';
+
+const regtest = regtestUtils.network;
+bitcoin.initEccLib(ecc);
+const bip32 = BIP32Factory(ecc);
+const rng = (size: number) => randomBytes(size);
+
+describe('bitcoinjs-lib (transaction with taproot)', () => {
+ it('can verify the BIP86 HD wallet vectors for taproot single sig (& sending example)', async () => {
+ // Values taken from BIP86 document
+ const mnemonic =
+ 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about';
+ const xprv =
+ 'xprv9s21ZrQH143K3GJpoapnV8SFfukcVBSfeCficPSGfubmSFDxo1kuHnLisriDvSnRRuL2Qrg5ggqHKNVpxR86QEC8w35uxmGoggxtQTPvfUu';
+ const path = `m/86'/0'/0'/0/0`; // Path to first child of receiving wallet on first account
+ const internalPubkey = Buffer.from(
+ 'cc8a4bc64d897bddc5fbc2f670f7a8ba0b386779106cf1223c6fc5d7cd6fc115',
+ 'hex',
+ );
+ const expectedAddress =
+ 'bc1p5cyxnuxmeuwuvkwfem96lqzszd02n6xdcjrs20cac6yqjjwudpxqkedrcr';
+
+ // Verify the above (Below is no different than other HD wallets)
+ const seed = await bip39.mnemonicToSeed(mnemonic);
+ const rootKey = bip32.fromSeed(seed);
+ assert.strictEqual(rootKey.toBase58(), xprv);
+ const childNode = rootKey.derivePath(path);
+ // Since internalKey is an xOnly pubkey, we drop the DER header byte
+ const childNodeXOnlyPubkey = toXOnly(childNode.publicKey);
+ assert.deepEqual(childNodeXOnlyPubkey, internalPubkey);
+
+ // This is new for taproot
+ // Note: we are using mainnet here to get the correct address
+ // The output is the same no matter what the network is.
+ const { address, output } = bitcoin.payments.p2tr({
+ internalPubkey,
+ });
+ assert.ok(!!output);
+ assert.strictEqual(address, expectedAddress);
+ // Used for signing, since the output and address are using a tweaked key
+ // We must tweak the signer in the same way.
+ const tweakedChildNode = childNode.tweak(
+ bitcoin.crypto.taggedHash('TapTweak', childNodeXOnlyPubkey),
+ );
+
+ // amount from faucet
+ const amount = 42e4;
+ // amount to send
+ const sendAmount = amount - 1e4;
+ // Send some sats to the address via faucet. Get the hash and index. (txid/vout)
+ const { txId: hash, vout: index } = await regtestUtils.faucetComplex(
+ Buffer.from(output),
+ amount,
+ );
+ // Sent 420000 sats to taproot address
+
+ const psbt = new bitcoin.Psbt({ network: regtest })
+ .addInput({
+ hash,
+ index,
+ witnessUtxo: { value: BigInt(amount), script: output },
+ tapInternalKey: childNodeXOnlyPubkey,
+ })
+ .addOutput({
+ value: BigInt(sendAmount),
+ address: regtestUtils.RANDOM_ADDRESS,
+ })
+ .signInput(0, tweakedChildNode)
+ .finalizeAllInputs();
+
+ const tx = psbt.extractTransaction();
+ await regtestUtils.broadcast(tx.toHex());
+ await regtestUtils.verify({
+ txId: tx.getId(),
+ address: regtestUtils.RANDOM_ADDRESS,
+ vout: 0,
+ value: sendAmount,
+ });
+ });
+
+ it('can create (and broadcast via 3PBP) a taproot key-path spend Transaction', async () => {
+ const internalKey = bip32.fromSeed(rng(64), regtest);
+ const p2pkhKey = bip32.fromSeed(rng(64), regtest);
+
+ const { output } = bitcoin.payments.p2tr({
+ internalPubkey: toXOnly(internalKey.publicKey),
+ network: regtest,
+ });
+
+ const { output: p2pkhOutput } = bitcoin.payments.p2pkh({
+ pubkey: p2pkhKey.publicKey,
+ network: regtest,
+ });
+
+ // amount from faucet
+ const amount = 42e4;
+ // amount to send
+ const sendAmount = amount - 1e4;
+ // get faucet
+ const unspent = await regtestUtils.faucetComplex(
+ Buffer.from(output!),
+ amount,
+ );
+
+ // non segwit utxo
+ const p2pkhUnspent = await regtestUtils.faucetComplex(
+ Buffer.from(p2pkhOutput!),
+ amount,
+ );
+ const utx = await regtestUtils.fetch(p2pkhUnspent.txId);
+ const nonWitnessUtxo = Buffer.from(utx.txHex, 'hex');
+
+ const psbt = new bitcoin.Psbt({ network: regtest });
+ psbt.addInput({
+ hash: unspent.txId,
+ index: 0,
+ witnessUtxo: { value: BigInt(amount), script: output! },
+ tapInternalKey: toXOnly(internalKey.publicKey),
+ });
+ psbt.addInput({ index: 0, hash: p2pkhUnspent.txId, nonWitnessUtxo });
+
+ const sendInternalKey = bip32.fromSeed(rng(64), regtest);
+ const sendPubKey = toXOnly(sendInternalKey.publicKey);
+ const { address: sendAddress } = bitcoin.payments.p2tr({
+ internalPubkey: sendPubKey,
+ network: regtest,
+ });
+
+ psbt.addOutput({
+ value: BigInt(sendAmount),
+ address: sendAddress!,
+ tapInternalKey: sendPubKey,
+ });
+
+ const tweakedSigner = internalKey.tweak(
+ bitcoin.crypto.taggedHash('TapTweak', toXOnly(internalKey.publicKey)),
+ );
+ await psbt.signInputAsync(0, tweakedSigner);
+ await psbt.signInputAsync(1, p2pkhKey);
+
+ psbt.finalizeAllInputs();
+ const tx = psbt.extractTransaction();
+ const rawTx = tx.toBuffer();
+
+ const hex = tools.toHex(rawTx);
+
+ await regtestUtils.broadcast(hex);
+ await regtestUtils.verify({
+ txId: tx.getId(),
+ address: sendAddress!,
+ vout: 0,
+ value: sendAmount,
+ });
+ });
+
+ it('can create (and broadcast via 3PBP) a taproot key-path spend Transaction (with unused scriptTree)', async () => {
+ const internalKey = bip32.fromSeed(rng(64), regtest);
+ const leafKey = bip32.fromSeed(rng(64), regtest);
+
+ const leafScriptAsm = `${tools.toHex(
+ toXOnly(leafKey.publicKey),
+ )} OP_CHECKSIG`;
+ const leafScript = bitcoin.script.fromASM(leafScriptAsm);
+
+ const scriptTree = {
+ output: leafScript,
+ };
+
+ const { output, address, hash } = bitcoin.payments.p2tr({
+ internalPubkey: toXOnly(internalKey.publicKey),
+ scriptTree,
+ network: regtest,
+ });
+
+ // amount from faucet
+ const amount = 42e4;
+ // amount to send
+ const sendAmount = amount - 1e4;
+ // get faucet
+ const unspent = await regtestUtils.faucetComplex(
+ Buffer.from(output!),
+ amount,
+ );
+
+ const psbt = new bitcoin.Psbt({ network: regtest });
+ psbt.addInput({
+ hash: unspent.txId,
+ index: 0,
+ witnessUtxo: { value: BigInt(amount), script: output! },
+ tapInternalKey: toXOnly(internalKey.publicKey),
+ tapMerkleRoot: hash,
+ });
+ psbt.addOutput({ value: BigInt(sendAmount), address: address! });
+
+ const tweakedSigner = internalKey.tweak(
+ bitcoin.crypto.taggedHash(
+ 'TapTweak',
+ Buffer.concat([toXOnly(internalKey.publicKey), hash!]),
+ ),
+ );
+ psbt.signInput(0, tweakedSigner);
+
+ psbt.finalizeAllInputs();
+ const tx = psbt.extractTransaction();
+ const rawTx = tx.toBuffer();
+
+ const hex = tools.toHex(rawTx);
+
+ await regtestUtils.broadcast(hex);
+ await regtestUtils.verify({
+ txId: tx.getId(),
+ address: address!,
+ vout: 0,
+ value: sendAmount,
+ });
+ });
+
+ it('can create (and broadcast via 3PBP) a taproot script-path spend Transaction - OP_CHECKSIG', async () => {
+ const internalKey = bip32.fromSeed(rng(64), regtest);
+ const leafKey = bip32.fromSeed(rng(64), regtest);
+
+ const leafScriptAsm = `${tools.toHex(
+ toXOnly(leafKey.publicKey),
+ )} OP_CHECKSIG`;
+ const leafScript = bitcoin.script.fromASM(leafScriptAsm);
+
+ const scriptTree: Taptree = [
+ [
+ {
+ output: bitcoin.script.fromASM(
+ '50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0 OP_CHECKSIG',
+ ),
+ },
+ [
+ {
+ output: bitcoin.script.fromASM(
+ '50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac1 OP_CHECKSIG',
+ ),
+ },
+ {
+ output: bitcoin.script.fromASM(
+ '2258b1c3160be0864a541854eec9164a572f094f7562628281a8073bb89173a7 OP_CHECKSIG',
+ ),
+ },
+ ],
+ ],
+ [
+ {
+ output: bitcoin.script.fromASM(
+ '50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac2 OP_CHECKSIG',
+ ),
+ },
+ [
+ {
+ output: bitcoin.script.fromASM(
+ '50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac3 OP_CHECKSIG',
+ ),
+ },
+ [
+ {
+ output: bitcoin.script.fromASM(
+ '50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac4 OP_CHECKSIG',
+ ),
+ },
+ {
+ output: leafScript,
+ },
+ ],
+ ],
+ ],
+ ];
+ const redeem = {
+ output: leafScript,
+ redeemVersion: LEAF_VERSION_TAPSCRIPT,
+ };
+
+ const { output, witness } = bitcoin.payments.p2tr({
+ internalPubkey: toXOnly(internalKey.publicKey),
+ scriptTree,
+ redeem,
+ network: regtest,
+ });
+
+ // amount from faucet
+ const amount = 42e4;
+ // amount to send
+ const sendAmount = amount - 1e4;
+ // get faucet
+ const unspent = await regtestUtils.faucetComplex(
+ Buffer.from(output!),
+ amount,
+ );
+
+ const psbt = new bitcoin.Psbt({ network: regtest });
+ psbt.addInput({
+ hash: unspent.txId,
+ index: 0,
+ witnessUtxo: { value: BigInt(amount), script: output! },
+ });
+ psbt.updateInput(0, {
+ tapLeafScript: [
+ {
+ leafVersion: redeem.redeemVersion,
+ script: redeem.output,
+ controlBlock: witness![witness!.length - 1],
+ },
+ ],
+ });
+
+ const sendInternalKey = bip32.fromSeed(rng(64), regtest);
+ const sendPubKey = toXOnly(sendInternalKey.publicKey);
+ const { address: sendAddress } = bitcoin.payments.p2tr({
+ internalPubkey: sendPubKey,
+ scriptTree,
+ network: regtest,
+ });
+
+ psbt.addOutput({
+ value: BigInt(sendAmount),
+ address: sendAddress!,
+ tapInternalKey: sendPubKey,
+ tapTree: { leaves: tapTreeToList(scriptTree) },
+ });
+
+ psbt.signInput(0, leafKey);
+ psbt.finalizeInput(0);
+ const tx = psbt.extractTransaction();
+ const rawTx = tx.toBuffer();
+ const hex = tools.toHex(rawTx);
+
+ await regtestUtils.broadcast(hex);
+ await regtestUtils.verify({
+ txId: tx.getId(),
+ address: sendAddress!,
+ vout: 0,
+ value: sendAmount,
+ });
+ });
+
+ it('can create (and broadcast via 3PBP) a taproot script-path spend Transaction - OP_CHECKSEQUENCEVERIFY', async () => {
+ const internalKey = bip32.fromSeed(rng(64), regtest);
+ const leafKey = bip32.fromSeed(rng(64), regtest);
+ const leafPubkey = tools.toHex(toXOnly(leafKey.publicKey));
+
+ const leafScriptAsm = `OP_10 OP_CHECKSEQUENCEVERIFY OP_DROP ${leafPubkey} OP_CHECKSIG`;
+ const leafScript = bitcoin.script.fromASM(leafScriptAsm);
+
+ const scriptTree: Taptree = [
+ {
+ output: bitcoin.script.fromASM(
+ '50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0 OP_CHECKSIG',
+ ),
+ },
+ [
+ {
+ output: bitcoin.script.fromASM(
+ '50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0 OP_CHECKSIG',
+ ),
+ },
+ {
+ output: leafScript,
+ },
+ ],
+ ];
+ const redeem = {
+ output: leafScript,
+ redeemVersion: LEAF_VERSION_TAPSCRIPT,
+ };
+
+ const { output, witness } = bitcoin.payments.p2tr({
+ internalPubkey: toXOnly(internalKey.publicKey),
+ scriptTree,
+ redeem,
+ network: regtest,
+ });
+
+ // amount from faucet
+ const amount = 42e4;
+ // amount to send
+ const sendAmount = amount - 1e4;
+ // get faucet
+ const unspent = await regtestUtils.faucetComplex(
+ Buffer.from(output!),
+ amount,
+ );
+
+ const psbt = new bitcoin.Psbt({ network: regtest });
+ psbt.addInput({
+ hash: unspent.txId,
+ index: 0,
+ sequence: 10,
+ witnessUtxo: { value: BigInt(amount), script: output! },
+ });
+ psbt.updateInput(0, {
+ tapLeafScript: [
+ {
+ leafVersion: redeem.redeemVersion,
+ script: redeem.output,
+ controlBlock: witness![witness!.length - 1],
+ },
+ ],
+ });
+
+ const sendInternalKey = bip32.fromSeed(rng(64), regtest);
+ const sendPubKey = toXOnly(sendInternalKey.publicKey);
+ const { address: sendAddress } = bitcoin.payments.p2tr({
+ internalPubkey: sendPubKey,
+ scriptTree,
+ network: regtest,
+ });
+
+ psbt.addOutput({ value: BigInt(sendAmount), address: sendAddress! });
+ // just to test that updateOutput works as expected
+ psbt.updateOutput(0, {
+ tapInternalKey: sendPubKey,
+ tapTree: { leaves: tapTreeToList(scriptTree) },
+ });
+
+ await psbt.signInputAsync(0, leafKey);
+
+ psbt.finalizeInput(0);
+ const tx = psbt.extractTransaction();
+ const rawTx = tx.toBuffer();
+ const hex = tools.toHex(rawTx);
+
+ try {
+ // broadcast before the confirmation period has expired
+ await regtestUtils.broadcast(hex);
+ throw new Error('Broadcast should fail.');
+ } catch (err) {
+ if ((err as any).message !== 'non-BIP68-final')
+ throw new Error(
+ 'Expected OP_CHECKSEQUENCEVERIFY validation to fail. But it failed with: ' +
+ err,
+ );
+ }
+ await regtestUtils.mine(10);
+ await regtestUtils.broadcast(hex);
+ await regtestUtils.verify({
+ txId: tx.getId(),
+ address: sendAddress!,
+ vout: 0,
+ value: sendAmount,
+ });
+ });
+
+ it('can create (and broadcast via 3PBP) a taproot script-path spend Transaction - OP_CHECKSIGADD (3-of-3)', async () => {
+ const internalKey = bip32.fromSeed(rng(64), regtest);
+
+ const leafKeys: BIP32Interface[] = [];
+ const leafPubkeys: string[] = [];
+ for (let i = 0; i < 3; i++) {
+ const leafKey = bip32.fromSeed(rng(64), regtest);
+ leafKeys.push(leafKey);
+ leafPubkeys.push(tools.toHex(toXOnly(leafKey.publicKey)));
+ }
+
+ const leafScriptAsm = `${leafPubkeys[2]} OP_CHECKSIG ${leafPubkeys[1]} OP_CHECKSIGADD ${leafPubkeys[0]} OP_CHECKSIGADD OP_3 OP_NUMEQUAL`;
+
+ const leafScript = bitcoin.script.fromASM(leafScriptAsm);
+
+ const scriptTree: Taptree = [
+ {
+ output: bitcoin.script.fromASM(
+ '50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0 OP_CHECKSIG',
+ ),
+ },
+ [
+ {
+ output: bitcoin.script.fromASM(
+ '50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0 OP_CHECKSIG',
+ ),
+ },
+ {
+ output: leafScript,
+ },
+ ],
+ ];
+ const redeem = {
+ output: leafScript,
+ redeemVersion: LEAF_VERSION_TAPSCRIPT,
+ };
+
+ const { output, address, witness } = bitcoin.payments.p2tr({
+ internalPubkey: toXOnly(internalKey.publicKey),
+ scriptTree,
+ redeem,
+ network: regtest,
+ });
+
+ // amount from faucet
+ const amount = 42e4;
+ // amount to send
+ const sendAmount = amount - 1e4;
+ // get faucet
+ const unspent = await regtestUtils.faucetComplex(
+ Buffer.from(output!),
+ amount,
+ );
+
+ const psbt = new bitcoin.Psbt({ network: regtest });
+ psbt.addInput({
+ hash: unspent.txId,
+ index: 0,
+ witnessUtxo: { value: BigInt(amount), script: output! },
+ });
+ psbt.updateInput(0, {
+ tapLeafScript: [
+ {
+ leafVersion: redeem.redeemVersion,
+ script: redeem.output,
+ controlBlock: witness![witness!.length - 1],
+ },
+ ],
+ });
+
+ psbt.addOutput({ value: BigInt(sendAmount), address: address! });
+
+ // random order for signers
+ psbt.signInput(0, leafKeys[1]);
+ psbt.signInput(0, leafKeys[2]);
+ psbt.signInput(0, leafKeys[0]);
+
+ psbt.finalizeInput(0);
+ const tx = psbt.extractTransaction();
+ const rawTx = tx.toBuffer();
+ const hex = tools.toHex(rawTx);
+
+ await regtestUtils.broadcast(hex);
+ await regtestUtils.verify({
+ txId: tx.getId(),
+ address: address!,
+ vout: 0,
+ value: sendAmount,
+ });
+ });
+
+ it('can create (and broadcast via 3PBP) a taproot script-path spend Transaction - OP_CHECKSIGADD (2-of-3) and verify unspendable internalKey', async () => {
+ const leafKeys: BIP32Interface[] = [];
+ const leafPubkeys: Uint8Array[] = [];
+ for (let i = 0; i < 3; i++) {
+ const leafKey = bip32.fromSeed(rng(64), regtest);
+ leafKeys.push(leafKey);
+ leafPubkeys.push(toXOnly(leafKey.publicKey));
+ }
+
+ // The only thing that differs between the wallets is the private key.
+ // So we will use the first wallet for all the Psbt stuff.
+ const [wallet, wallet2, wallet3] = leafKeys.map(key =>
+ new TaprootMultisigWallet(
+ leafPubkeys,
+ 2, // Number of required signatures
+ key.privateKey!,
+ LEAF_VERSION_TAPSCRIPT,
+ ).setNetwork(regtest),
+ );
+
+ // amount from faucet
+ const amount = 42e4;
+ // amount to send
+ const sendAmount = amount - 1e4;
+ // get faucet
+ const unspent = await regtestUtils.faucetComplex(
+ Buffer.from(wallet.output),
+ amount,
+ );
+
+ const psbt = new bitcoin.Psbt({ network: regtest });
+
+ // Adding an input is a bit special in this case,
+ // So we contain it in the wallet class
+ // Any wallet can do this, wallet2 or wallet3 could be used.
+ wallet.addInput(psbt, unspent.txId, unspent.vout, BigInt(unspent.value));
+
+ psbt.addOutput({ value: BigInt(sendAmount), address: wallet.address });
+
+ // Sign with at least 2 of the 3 wallets.
+ // Verify that there is a matching leaf script
+ // (which includes the unspendable internalPubkey,
+ // so we verify that no one can key-spend it)
+ wallet3.verifyInputScript(psbt, 0);
+ wallet2.verifyInputScript(psbt, 0);
+ psbt.signInput(0, wallet3);
+ psbt.signInput(0, wallet2);
+
+ // Before finalizing, we need to add dummy signatures for all that did not sign.
+ // Any wallet can do this, wallet2 or wallet3 could be used.
+ wallet.addDummySigs(psbt);
+
+ psbt.finalizeAllInputs();
+ const tx = psbt.extractTransaction();
+ const rawTx = tx.toBuffer();
+ const hex = tools.toHex(rawTx);
+
+ await regtestUtils.broadcast(hex);
+ await regtestUtils.verify({
+ txId: tx.getId(),
+ // Any wallet can do this, wallet2 or wallet3 could be used.
+ address: wallet.address,
+ vout: 0,
+ value: sendAmount,
+ });
+ });
+
+ it('can create (and broadcast via 3PBP) a taproot script-path spend Transaction - custom finalizer', async () => {
+ const leafCount = 8;
+ const leaves = Array.from({ length: leafCount }).map(
+ (_, index) =>
+ ({
+ depth: 3,
+ leafVersion: LEAF_VERSION_TAPSCRIPT,
+ script: bitcoin.script.fromASM(`OP_ADD OP_${index * 2} OP_EQUAL`),
+ }) as TapLeaf,
+ );
+ const scriptTree = tapTreeFromList(leaves);
+
+ for (let leafIndex = 1; leafIndex < leafCount; leafIndex++) {
+ const redeem = {
+ output: bitcoin.script.fromASM(`OP_ADD OP_${leafIndex * 2} OP_EQUAL`),
+ redeemVersion: LEAF_VERSION_TAPSCRIPT,
+ };
+
+ const internalKey = bip32.fromSeed(rng(64), regtest);
+ const { output, witness } = bitcoin.payments.p2tr({
+ internalPubkey: toXOnly(internalKey.publicKey),
+ scriptTree,
+ redeem,
+ network: regtest,
+ });
+
+ // amount from faucet
+ const amount = 42e4;
+ // amount to send
+ const sendAmount = amount - 1e4;
+ // get faucet
+ const unspent = await regtestUtils.faucetComplex(
+ Buffer.from(output!),
+ amount,
+ );
+
+ const psbt = new bitcoin.Psbt({ network: regtest });
+ psbt.addInput({
+ hash: unspent.txId,
+ index: 0,
+ witnessUtxo: { value: BigInt(amount), script: output! },
+ });
+
+ const tapLeafScript: TapLeafScript = {
+ leafVersion: redeem.redeemVersion,
+ script: redeem.output,
+ controlBlock: witness![witness!.length - 1],
+ };
+ psbt.updateInput(0, { tapLeafScript: [tapLeafScript] });
+
+ const sendAddress =
+ 'bcrt1pqknex3jwpsaatu5e5dcjw70nac3fr5k5y3hcxr4hgg6rljzp59nqs6a0vh';
+ psbt.addOutput({
+ value: BigInt(sendAmount),
+ address: sendAddress,
+ });
+
+ const leafIndexFinalizerFn = buildLeafIndexFinalizer(
+ tapLeafScript,
+ leafIndex,
+ );
+ psbt.finalizeInput(0, leafIndexFinalizerFn);
+ const tx = psbt.extractTransaction();
+ const rawTx = tx.toBuffer();
+ const hex = tools.toHex(rawTx);
+
+ await regtestUtils.broadcast(hex);
+ await regtestUtils.verify({
+ txId: tx.getId(),
+ address: sendAddress!,
+ vout: 0,
+ value: sendAmount,
+ });
+ }
+ });
+
+ it('should fail validating invalid signatures for taproot (See issue #1931)', () => {
+ const schnorrValidator = (
+ pubkey: Uint8Array,
+ msghash: Uint8Array,
+ signature: Uint8Array,
+ ) => {
+ return ecc.verifySchnorr(msghash, pubkey, signature);
+ };
+
+ const psbtBase64 =
+ `cHNidP8BAFICAAAAAe1h73A6zedruNERV6JU7Ty1IlYZh2KO1cBklZqCMEy8AAAAAAD/////ARA
+ nAAAAAAAAFgAUS0GlfqWSeEWIpwPwrvRIjBbJQroAAAAAAAEA/TgBAQAAAAABAnGJ6st1FIvYLEV
+ bJMQaZ3HSOJnkw5C+ViCuJYiFEYosAAAAAAD9////xuZd0xArNSaBuElLX3nzjwtZW95O7L/wbz9
+ 4v+v0vuYAAAAAAP3///8CECcAAAAAAAAiUSAVbMSHgwYVdyBgfNy0syr6TMaFOGhFjXJYuQcRLlp
+ DS8hgBwAAAAAAIlEgthWGz3o2R7WpgjIK52ODoEaA/0HcImSUjVk6agZgghwBQIP9WWErMfeBBYy
+ uHuSZS7MdXVICtlFgNveDrvuXeQGSZl1gGG6/r3Aw7h9TifGtoA+7JwYBjLMcEG6hbeyQGXIBQNS
+ qKH1p/NFzO9bxe9vpvBZQIaX5Qa9SY2NfNCgSRNabmX5EiaihWcLC+ALgchm7DUfYrAmi1r4uSI/
+ YaQ1lq8gAAAAAAQErECcAAAAAAAAiUSAVbMSHgwYVdyBgfNy0syr6TMaFOGhFjXJYuQcRLlpDSwE
+ DBIMAAAABCEMBQZUpv6e1Hwfpi/PpglkkK/Rx40vZIIHwtJ7dXWFZ5TcZUEelCnfKOAWZ4xWjauY
+ M2y+JcgFcVsuPzPuiM+z5AH+DARNBlSm/p7UfB+mL8+mCWSQr9HHjS9kggfC0nt1dYVnlNxlQR6U
+ Kd8o4BZnjFaNq5gzbL4lyAVxWy4/M+6Iz7PkAf4MBFyC6ZCT2zZVrEbkw/T1fyS8eLKQaP2MH6rz
+ dlMauGvQzLQAA`.replace(/\s+/g, '');
+
+ const psbt = bitcoin.Psbt.fromBase64(psbtBase64);
+
+ assert.ok(
+ !psbt.validateSignaturesOfAllInputs(schnorrValidator),
+ 'Should fail validation',
+ );
+ });
+
+ it('should succeed validating valid signatures for taproot (See issue #1934)', () => {
+ const schnorrValidator = (
+ pubkey: Uint8Array,
+ msghash: Uint8Array,
+ signature: Uint8Array,
+ ) => {
+ return ecc.verifySchnorr(msghash, pubkey, signature);
+ };
+
+ const psbtBase64 =
+ `cHNidP8BAF4CAAAAAU6UzYPa7tES0HoS+obnRJuXX41Ob64Zs59qDEyKsu1ZAAAAAAD/////AYA
+ zAjsAAAAAIlEgIlIzfR+flIWYTyewD9v+1N84IubZ/7qg6oHlYLzv1aYAAAAAAAEAXgEAAAAB8f+
+ afEJBun7sRQLFE1Olc/gK9LBaduUpz3vB4fjXVF0AAAAAAP3///8BECcAAAAAAAAiUSAiUjN9H5+
+ UhZhPJ7AP2/7U3zgi5tn/uqDqgeVgvO/VpgAAAAABASsQJwAAAAAAACJRICJSM30fn5SFmE8nsA/
+ b/tTfOCLm2f+6oOqB5WC879WmAQMEgwAAAAETQWQwNOao3RMOBWPuAQ9Iph7Qzk47MvroTHbJR49
+ MxKJmQ6hfhZa5wVVrdKYea5BW/loqa7al2pYYZMlGvdS06wODARcgjuYXxIpyOMVTYEvl35gDidC
+ m/vUICZyuNNZKaPz9dxAAAQUgjuYXxIpyOMVTYEvl35gDidCm/vUICZyuNNZKaPz9dxAA`.replace(
+ /\s+/g,
+ '',
+ );
+
+ const psbt = bitcoin.Psbt.fromBase64(psbtBase64);
+
+ assert.ok(
+ psbt.validateSignaturesOfAllInputs(schnorrValidator),
+ 'Should succeed validation',
+ );
+ });
+});
+
+function buildLeafIndexFinalizer(
+ tapLeafScript: TapLeafScript,
+ leafIndex: number,
+): (
+ inputIndex: number,
+ _input: PsbtInput,
+ _tapLeafHashToFinalize?: Uint8Array,
+) => {
+ finalScriptWitness: Uint8Array | undefined;
+} {
+ return (
+ inputIndex: number,
+ _input: PsbtInput,
+ _tapLeafHashToFinalize?: Uint8Array,
+ ): {
+ finalScriptWitness: Uint8Array | undefined;
+ } => {
+ try {
+ const scriptSolution = [
+ Uint8Array.from([leafIndex]),
+ Uint8Array.from([leafIndex]),
+ ];
+ const witness = scriptSolution
+ .concat(tapLeafScript.script)
+ .concat(tapLeafScript.controlBlock);
+ return { finalScriptWitness: witnessStackToScriptWitness(witness) };
+ } catch (err) {
+ throw new Error(`Can not finalize taproot input #${inputIndex}: ${err}`);
+ }
+ };
+}
+
+function makeUnspendableInternalKey(provableNonce?: Uint8Array): Uint8Array {
+ // This is the generator point of secp256k1. Private key is known (equal to 1)
+ const G = Buffer.from(
+ '0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8',
+ 'hex',
+ );
+ // This is the hash of the uncompressed generator point.
+ // It is also a valid X value on the curve, but we don't know what the private key is.
+ // Since we know this X value (a fake "public key") is made from a hash of a well known value,
+ // We can prove that the internalKey is unspendable.
+ const Hx = sha256(G);
+
+ // This "Nothing Up My Sleeve" value is mentioned in BIP341 so we verify it here:
+ assert.strictEqual(
+ tools.toHex(Hx),
+ '50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0',
+ );
+
+ if (provableNonce) {
+ if (provableNonce.length !== 32) {
+ throw new Error(
+ 'provableNonce must be a 32 byte random value shared between script holders',
+ );
+ }
+ // Using a shared random value, we create an unspendable internalKey
+ // P = H + int(hash_taptweak(provableNonce))*G
+ // Since we don't know H's private key (see explanation above), we can't know P's private key
+ const tapHash = bitcoin.crypto.taggedHash('TapTweak', provableNonce);
+ const ret = ecc.xOnlyPointAddTweak(Hx, tapHash);
+ if (!ret) {
+ throw new Error(
+ 'provableNonce produced an invalid key when tweaking the G hash',
+ );
+ }
+ return Buffer.from(ret.xOnlyPubkey);
+ } else {
+ // The downside to using no shared provable nonce is that anyone viewing a spend
+ // on the blockchain can KNOW that you CAN'T use key spend.
+ // Most people would be ok with this being public, but some wallets (exchanges etc)
+ // might not want ANY details about how their wallet works public.
+ return Hx;
+ }
+}
+
+class TaprootMultisigWallet {
+ private leafScriptCache: Uint8Array | null = null;
+ private internalPubkeyCache: Uint8Array | null = null;
+ private paymentCache: bitcoin.Payment | null = null;
+ private readonly publicKeyCache: Uint8Array;
+ network: bitcoin.Network;
+
+ constructor(
+ /**
+ * A list of all the (x-only) pubkeys in the multisig
+ */
+ private readonly pubkeys: Uint8Array[],
+ /**
+ * The number of required signatures
+ */
+ private readonly requiredSigs: number,
+ /**
+ * The private key you hold.
+ */
+ private readonly privateKey: Uint8Array,
+ /**
+ * leaf version (0xc0 currently)
+ */
+ readonly leafVersion: number,
+ /**
+ * Optional shared nonce. This should be used in wallets where
+ * the fact that key-spend is unspendable should not be public,
+ * BUT each signer must verify that it is unspendable to be safe.
+ */
+ private readonly sharedNonce?: Uint8Array,
+ ) {
+ this.network = bitcoin.networks.bitcoin;
+ assert.ok(pubkeys.length > 0, 'Need pubkeys');
+ assert.ok(
+ pubkeys.every(p => p.length === 32),
+ 'Pubkeys must be 32 bytes (x-only)',
+ );
+ assert.ok(
+ requiredSigs > 0 && requiredSigs <= pubkeys.length,
+ 'Invalid requiredSigs',
+ );
+
+ assert.ok(
+ leafVersion <= 0xff && (leafVersion & 1) === 0,
+ 'Invalid leafVersion',
+ );
+
+ if (sharedNonce) {
+ assert.ok(
+ sharedNonce.length === 32 && ecc.isPrivate(sharedNonce),
+ 'Invalid sharedNonce',
+ );
+ }
+
+ const pubkey = ecc.pointFromScalar(privateKey);
+ assert.ok(pubkey, 'Invalid pubkey');
+
+ this.publicKeyCache = Buffer.from(pubkey);
+ assert.ok(
+ pubkeys.some(p => tools.compare(p, toXOnly(this.publicKeyCache))),
+ 'At least one pubkey must match your private key',
+ );
+
+ // IMPORTANT: Make sure the pubkeys are sorted (To prevent ordering issues between wallet signers)
+ this.pubkeys.sort((a, b) => tools.compare(a, b));
+ }
+
+ setNetwork(network: bitcoin.Network): this {
+ this.network = network;
+ return this;
+ }
+
+ // Required for Signer interface.
+ // Prevent setting by using a getter.
+ get publicKey(): Uint8Array {
+ return this.publicKeyCache;
+ }
+
+ /**
+ * Lazily build the leafScript. A 2 of 3 would look like:
+ * key1 OP_CHECKSIG key2 OP_CHECKSIGADD key3 OP_CHECKSIGADD OP_2 OP_GREATERTHANOREQUAL
+ */
+ get leafScript(): Uint8Array {
+ if (this.leafScriptCache) {
+ return this.leafScriptCache;
+ }
+ const ops: bitcoin.Stack = [];
+ this.pubkeys.forEach(pubkey => {
+ if (ops.length === 0) {
+ ops.push(pubkey);
+ ops.push(bitcoin.opcodes.OP_CHECKSIG);
+ } else {
+ ops.push(pubkey);
+ ops.push(bitcoin.opcodes.OP_CHECKSIGADD);
+ }
+ });
+ if (this.requiredSigs > 16) {
+ ops.push(bitcoin.script.number.encode(this.requiredSigs));
+ } else {
+ ops.push(bitcoin.opcodes.OP_1 - 1 + this.requiredSigs);
+ }
+ ops.push(bitcoin.opcodes.OP_GREATERTHANOREQUAL);
+
+ this.leafScriptCache = bitcoin.script.compile(ops);
+ return this.leafScriptCache;
+ }
+
+ get internalPubkey(): Uint8Array {
+ if (this.internalPubkeyCache) {
+ return this.internalPubkeyCache;
+ }
+ // See the helper function for explanation
+ this.internalPubkeyCache = makeUnspendableInternalKey(this.sharedNonce);
+ return this.internalPubkeyCache;
+ }
+
+ get scriptTree(): Taptree {
+ // If more complicated, maybe it should be cached.
+ // (ie. if other scripts are created only to create the tree
+ // and will only be stored in the tree.)
+ return {
+ output: this.leafScript,
+ };
+ }
+
+ get redeem(): {
+ output: Uint8Array;
+ redeemVersion: number;
+ } {
+ return {
+ output: this.leafScript,
+ redeemVersion: this.leafVersion,
+ };
+ }
+
+ private get payment(): bitcoin.Payment {
+ if (this.paymentCache) {
+ return this.paymentCache;
+ }
+ this.paymentCache = bitcoin.payments.p2tr({
+ internalPubkey: this.internalPubkey,
+ scriptTree: this.scriptTree,
+ redeem: this.redeem,
+ network: this.network,
+ });
+ return this.paymentCache;
+ }
+
+ get output(): Uint8Array {
+ return this.payment.output!;
+ }
+
+ get address(): string {
+ return this.payment.address!;
+ }
+
+ get controlBlock(): Uint8Array {
+ const witness = this.payment.witness!;
+ return witness[witness.length - 1];
+ }
+
+ verifyInputScript(psbt: bitcoin.Psbt, index: number) {
+ if (index >= psbt.data.inputs.length)
+ throw new Error('Invalid input index');
+ const input = psbt.data.inputs[index];
+ if (!input.tapLeafScript) throw new Error('Input has no tapLeafScripts');
+ const hasMatch =
+ input.tapLeafScript.length === 1 &&
+ input.tapLeafScript[0].leafVersion === this.leafVersion &&
+ tools.compare(input.tapLeafScript[0].script, this.leafScript) === 0 &&
+ tools.compare(input.tapLeafScript[0].controlBlock, this.controlBlock) ===
+ 0;
+ if (!hasMatch)
+ throw new Error(
+ 'No matching leafScript, or extra leaf script. Refusing to sign.',
+ );
+ }
+
+ addInput(
+ psbt: bitcoin.Psbt,
+ hash: string | Buffer,
+ index: number,
+ value: bigint,
+ ) {
+ psbt.addInput({
+ hash,
+ index,
+ witnessUtxo: { value, script: this.output },
+ });
+ psbt.updateInput(psbt.inputCount - 1, {
+ tapLeafScript: [
+ {
+ leafVersion: this.leafVersion,
+ script: this.leafScript,
+ controlBlock: this.controlBlock,
+ },
+ ],
+ });
+ }
+
+ addDummySigs(psbt: bitcoin.Psbt) {
+ const leafHash = tapleafHash({
+ output: this.leafScript,
+ version: this.leafVersion,
+ });
+ for (const input of psbt.data.inputs) {
+ if (!input.tapScriptSig) continue;
+ const signedPubkeys = input.tapScriptSig
+ .filter(ts => tools.compare(ts.leafHash, leafHash) === 0)
+ .map(ts => ts.pubkey);
+ for (const pubkey of this.pubkeys) {
+ if (signedPubkeys.some(sPub => tools.compare(sPub, pubkey) === 0))
+ continue;
+ // Before finalizing, every key that did not sign must have an empty signature
+ // in place where their signature would be.
+ // In order to do this currently we need to construct a dummy signature manually.
+ input.tapScriptSig.push({
+ // This can be reused for each dummy signature
+ leafHash,
+ // This is the pubkey that didn't sign
+ pubkey,
+ // This must be an empty Buffer.
+ signature: Buffer.from([]),
+ });
+ }
+ }
+ }
+
+ // required for Signer interface
+ sign(hash: Uint8Array, _lowR?: boolean): Uint8Array {
+ return ecc.sign(hash, this.privateKey);
+ }
+
+ // required for Signer interface
+ signSchnorr(hash: Uint8Array): Uint8Array {
+ return ecc.signSchnorr(hash, this.privateKey);
+ }
+}
diff --git a/test/integration/transactions.spec.ts b/test/integration/transactions.spec.ts
index d4788dcb7..bf2e64ef7 100644
--- a/test/integration/transactions.spec.ts
+++ b/test/integration/transactions.spec.ts
@@ -1,16 +1,23 @@
import * as assert from 'assert';
-import * as bip32 from 'bip32';
-import { ECPair } from 'ecpair';
+import BIP32Factory from 'bip32';
+import * as ecc from 'tiny-secp256k1';
+import ECPairFactory from 'ecpair';
import { describe, it } from 'mocha';
-import * as bitcoin from '../..';
-import { regtestUtils } from './_regtest';
-const rng = require('randombytes');
+import * as bitcoin from 'bitcoinjs-lib';
+import { regtestUtils } from './_regtest.js';
+import * as tools from 'uint8array-tools';
+import { randomBytes } from 'crypto';
+
+const ECPair = ECPairFactory(ecc);
+// const rng = require('randombytes');
+const rng = (size: number) => randomBytes(size);
const regtest = regtestUtils.network;
+const bip32 = BIP32Factory(ecc);
const validator = (
- pubkey: Buffer,
- msghash: Buffer,
- signature: Buffer,
+ pubkey: Uint8Array,
+ msghash: Uint8Array,
+ signature: Uint8Array,
): boolean => ECPair.fromPublicKey(pubkey).verify(msghash, signature);
// See bottom of file for some helper functions used to make the payment objects needed.
@@ -63,7 +70,7 @@ describe('bitcoinjs-lib (transactions with psbt)', () => {
});
psbt.addOutput({
address: '1KRMKfeZcmosxALVYESdPNez1AP1mEtywp',
- value: 80000,
+ value: 80000n,
});
psbt.signInput(0, alice);
psbt.validateSignaturesOfInput(0, validator);
@@ -113,16 +120,16 @@ describe('bitcoinjs-lib (transactions with psbt)', () => {
.addInput(inputData2) // alice2 unspent
.addOutput({
address: 'mwCwTceJvYV27KXBc3NJZys6CjsgsoeHmf',
- value: 8e4,
+ value: BigInt(8e4),
}) // the actual "spend"
.addOutput({
address: alice2.payment.address, // OR script, which is a Buffer.
- value: 1e4,
+ value: BigInt(1e4),
}); // Alice's change
// (in)(5e4 + 7e4) - (out)(8e4 + 1e4) = (fee)3e4 = 30000, this is the miner fee
// Let's show a new feature with PSBT.
- // We can have multiple signers sign in parrallel and combine them.
+ // We can have multiple signers sign in parallel and combine them.
// (this is not necessary, but a nice feature)
// encode to send out to the signers
@@ -182,11 +189,11 @@ describe('bitcoinjs-lib (transactions with psbt)', () => {
.addInput(inputData1)
.addOutput({
script: embed.output!,
- value: 1000,
+ value: 1000n,
})
.addOutput({
address: regtestUtils.RANDOM_ADDRESS,
- value: 1e5,
+ value: BigInt(1e5),
})
.signInput(0, alice1.keys[0]);
@@ -217,7 +224,7 @@ describe('bitcoinjs-lib (transactions with psbt)', () => {
.addInput(inputData1)
.addOutput({
address: regtestUtils.RANDOM_ADDRESS,
- value: 1e4,
+ value: BigInt(1e4),
})
.signInput(0, multisig.keys[0])
.signInput(0, multisig.keys[2]);
@@ -264,11 +271,11 @@ describe('bitcoinjs-lib (transactions with psbt)', () => {
const keyPair = p2sh.keys[0];
const outputData = {
script: p2sh.payment.output, // sending to myself for fun
- value: 2e4,
+ value: BigInt(2e4),
};
const outputData2 = {
script: p2sh.payment.output, // sending to myself for fun
- value: 7e4,
+ value: BigInt(7e4),
};
const tx = new bitcoin.Psbt()
@@ -298,11 +305,11 @@ describe('bitcoinjs-lib (transactions with psbt)', () => {
const keyPair = p2sh.keys[0];
const outputData = {
script: p2sh.payment.output,
- value: 2e4,
+ value: BigInt(2e4),
};
const outputData2 = {
script: p2sh.payment.output,
- value: 7e4,
+ value: BigInt(7e4),
};
const tx = new bitcoin.Psbt()
.addInputs([inputData, inputData2])
@@ -333,7 +340,7 @@ describe('bitcoinjs-lib (transactions with psbt)', () => {
.addInput(inputData)
.addOutput({
address: regtestUtils.RANDOM_ADDRESS,
- value: 2e4,
+ value: BigInt(2e4),
})
.signInput(0, p2wpkh.keys[0]);
@@ -367,7 +374,7 @@ describe('bitcoinjs-lib (transactions with psbt)', () => {
.addInput(inputData)
.addOutput({
address: regtestUtils.RANDOM_ADDRESS,
- value: 2e4,
+ value: BigInt(2e4),
})
.signInput(0, p2wpkh.keys[0]);
psbt.finalizeAllInputs();
@@ -401,7 +408,7 @@ describe('bitcoinjs-lib (transactions with psbt)', () => {
.addInput(inputData)
.addOutput({
address: regtestUtils.RANDOM_ADDRESS,
- value: 2e4,
+ value: BigInt(2e4),
})
.signInput(0, p2wsh.keys[0]);
@@ -430,7 +437,7 @@ describe('bitcoinjs-lib (transactions with psbt)', () => {
.addInput(inputData)
.addOutput({
address: regtestUtils.RANDOM_ADDRESS,
- value: 2e4,
+ value: BigInt(2e4),
})
.signInput(0, p2wsh.keys[0]);
psbt.finalizeAllInputs();
@@ -444,6 +451,68 @@ describe('bitcoinjs-lib (transactions with psbt)', () => {
});
});
+ it(
+ 'can create (and broadcast via 3PBP) a Transaction, w/ a ' +
+ 'P2WSH(P2MS(20 of 20)) input',
+ async () => {
+ const keys = [];
+ for (let i = 0; i < 20; i++) {
+ keys.push(ECPair.makeRandom({ network: regtest, rng }));
+ }
+
+ const multisig = createPayment('p2wsh-p2ms(20 of 20)', keys);
+
+ const inputData = await getInputData(
+ 5e5,
+ multisig.payment,
+ true,
+ 'p2wsh',
+ );
+ {
+ const { hash, index, witnessUtxo, witnessScript } = inputData;
+ assert.deepStrictEqual(
+ { hash, index, witnessUtxo, witnessScript },
+ inputData,
+ );
+ }
+
+ const psbt = new bitcoin.Psbt({ network: regtest })
+ .addInput(inputData)
+ .addOutput({
+ address: regtestUtils.RANDOM_ADDRESS,
+ value: BigInt(3e5),
+ });
+
+ for (let i = 0; i < 20; i++) {
+ psbt.signInput(0, multisig.keys[i]);
+ }
+
+ for (let i = 0; i < 20; i++) {
+ assert.strictEqual(
+ psbt.validateSignaturesOfInput(
+ 0,
+ validator,
+ multisig.keys[i].publicKey,
+ ),
+ true,
+ );
+ }
+
+ psbt.finalizeAllInputs();
+
+ const tx = psbt.extractTransaction();
+
+ await regtestUtils.broadcast(tx.toHex());
+
+ await regtestUtils.verify({
+ txId: tx.getId(),
+ address: regtestUtils.RANDOM_ADDRESS,
+ vout: 0,
+ value: 3e5,
+ });
+ },
+ );
+
it(
'can create (and broadcast via 3PBP) a Transaction, w/ a ' +
'P2SH(P2WSH(P2MS(3 of 4))) (SegWit multisig) input',
@@ -456,13 +525,8 @@ describe('bitcoinjs-lib (transactions with psbt)', () => {
'p2sh-p2wsh',
);
{
- const {
- hash,
- index,
- witnessUtxo,
- redeemScript,
- witnessScript,
- } = inputData;
+ const { hash, index, witnessUtxo, redeemScript, witnessScript } =
+ inputData;
assert.deepStrictEqual(
{ hash, index, witnessUtxo, redeemScript, witnessScript },
inputData,
@@ -473,7 +537,7 @@ describe('bitcoinjs-lib (transactions with psbt)', () => {
.addInput(inputData)
.addOutput({
address: regtestUtils.RANDOM_ADDRESS,
- value: 2e4,
+ value: BigInt(2e4),
})
.signInput(0, p2sh.keys[0])
.signInput(0, p2sh.keys[2])
@@ -520,7 +584,7 @@ describe('bitcoinjs-lib (transactions with psbt)', () => {
.addInput(inputData)
.addOutput({
address: regtestUtils.RANDOM_ADDRESS,
- value: 2e4,
+ value: BigInt(2e4),
})
.signInput(0, p2sh.keys[0])
.signInput(0, p2sh.keys[2])
@@ -541,7 +605,7 @@ describe('bitcoinjs-lib (transactions with psbt)', () => {
'can create (and broadcast via 3PBP) a Transaction, w/ a ' +
'P2SH(P2MS(2 of 2)) input with nonWitnessUtxo',
async () => {
- const myKey = ECPair.makeRandom({ network: regtest });
+ const myKey = ECPair.makeRandom({ network: regtest, rng });
const myKeys = [
myKey,
ECPair.fromPrivateKey(myKey.privateKey!, { network: regtest }),
@@ -552,7 +616,7 @@ describe('bitcoinjs-lib (transactions with psbt)', () => {
.addInput(inputData)
.addOutput({
address: regtestUtils.RANDOM_ADDRESS,
- value: 2e4,
+ value: BigInt(2e4),
})
.signInput(0, p2sh.keys[0]);
psbt.finalizeAllInputs();
@@ -606,7 +670,7 @@ describe('bitcoinjs-lib (transactions with psbt)', () => {
// .updateInput(0, updateData) // if you didn't merge the bip32Derivation with inputData
.addOutput({
address: regtestUtils.RANDOM_ADDRESS,
- value: 2e4,
+ value: BigInt(2e4),
})
.signInputHD(0, hdRoot); // must sign with root!!!
@@ -645,18 +709,20 @@ function createPayment(_type: string, myKeys?: any[], network?: any): any {
throw new Error('Need n keys for multisig');
}
while (!myKeys && n > 1) {
- keys.push(ECPair.makeRandom({ network }));
+ keys.push(ECPair.makeRandom({ network, rng }));
n--;
}
}
- if (!myKeys) keys.push(ECPair.makeRandom({ network }));
+ if (!myKeys) keys.push(ECPair.makeRandom({ network, rng }));
let payment: any;
splitType.forEach(type => {
if (type.slice(0, 4) === 'p2ms') {
payment = bitcoin.payments.p2ms({
m,
- pubkeys: keys.map(key => key.publicKey).sort((a, b) => a.compare(b)),
+ pubkeys: keys
+ .map(key => key.publicKey)
+ .sort((a, b) => tools.compare(a, b)),
network,
});
} else if (['p2sh', 'p2wsh'].indexOf(type) > -1) {
@@ -680,7 +746,8 @@ function createPayment(_type: string, myKeys?: any[], network?: any): any {
function getWitnessUtxo(out: any): any {
delete out.address;
- out.script = Buffer.from(out.script, 'hex');
+ out.script = tools.fromHex(out.script);
+ out.value = BigInt(out.value);
return out;
}
@@ -690,7 +757,10 @@ async function getInputData(
isSegwit: boolean,
redeemType: string,
): Promise {
- const unspent = await regtestUtils.faucetComplex(payment.output, amount);
+ const unspent = await regtestUtils.faucetComplex(
+ Buffer.from(payment.output),
+ amount,
+ );
const utx = await regtestUtils.fetch(unspent.txId);
// for non segwit inputs, you must pass the full transaction buffer
const nonWitnessUtxo = Buffer.from(utx.txHex, 'hex');
diff --git a/test/ops.spec.ts b/test/ops.spec.ts
new file mode 100644
index 000000000..7a3c8af5f
--- /dev/null
+++ b/test/ops.spec.ts
@@ -0,0 +1,47 @@
+import { describe, it } from 'mocha';
+import assert from 'assert';
+import { opcodes as OPS } from 'bitcoinjs-lib';
+
+describe('OPS Enum Tests', () => {
+ it('should map OPS keys to correct numbers and reverse lookup', () => {
+ Object.keys(OPS)
+ .filter(key => isNaN(Number(key))) // Only test enum keys, not reverse-mapped numbers
+ .forEach(key => {
+ const value = OPS[key as keyof typeof OPS];
+
+ // Assert the forward mapping
+ assert.strictEqual(
+ OPS[key],
+ value,
+ `Failed for key: ${key}, value: ${value}`,
+ );
+ });
+ });
+
+ it('should reverse map numbers to correct keys', () => {
+ const valueToKeysMap = new Map();
+
+ Object.keys(OPS)
+ .filter(key => isNaN(Number(key)))
+ .forEach(key => {
+ const value = OPS[key as keyof typeof OPS];
+ if (!valueToKeysMap.has(value)) {
+ valueToKeysMap.set(value, []);
+ }
+ valueToKeysMap.get(value)!.push(key);
+ });
+
+ Object.values(OPS)
+ .filter(value => typeof value === 'number')
+ .forEach(value => {
+ const keys = valueToKeysMap.get(value) || [];
+ keys.forEach(key => {
+ assert.strictEqual(
+ OPS[key],
+ value,
+ `Failed for value: ${value}, key: ${key}`,
+ );
+ });
+ });
+ });
+});
diff --git a/test/payments.spec.ts b/test/payments.spec.ts
index bc123cba3..d7128c0ab 100644
--- a/test/payments.spec.ts
+++ b/test/payments.spec.ts
@@ -1,29 +1,94 @@
import * as assert from 'assert';
-import { describe, it } from 'mocha';
-import { PaymentCreator } from '../src/payments';
-import * as u from './payments.utils';
-['embed', 'p2ms', 'p2pk', 'p2pkh', 'p2sh', 'p2wpkh', 'p2wsh'].forEach(p => {
- describe(p, () => {
- let fn: PaymentCreator;
- const payment = require('../src/payments/' + p);
- if (p === 'embed') {
- fn = payment.p2data;
- } else {
- fn = payment[p];
- }
- const fixtures = require('./fixtures/' + p);
+import * as ecc from 'tiny-secp256k1';
+import { describe, it, before, beforeEach } from 'mocha';
+import { PaymentCreator } from 'bitcoinjs-lib';
+import * as u from './payments.utils.js';
+import { initEccLib } from 'bitcoinjs-lib';
+import { payments } from 'bitcoinjs-lib';
+const {
+ embed: p2data,
+ p2ms,
+ p2pk,
+ p2pkh,
+ p2sh,
+ p2wpkh,
+ p2wsh,
+ p2tr,
+} = payments;
+
+import embedFixtures from './fixtures/embed.json';
+import p2msFixtures from './fixtures/p2ms.json';
+import p2pkFixtures from './fixtures/p2pk.json';
+import p2pkhFixtures from './fixtures/p2pkh.json';
+import p2shFixtures from './fixtures/p2sh.json';
+import p2wpkhFixtures from './fixtures/p2wpkh.json';
+import p2wshFixtures from './fixtures/p2wsh.json';
+import p2trFixtures from './fixtures/p2tr.json';
+
+let testSuite: {
+ paymentName: string;
+ fixtures: { valid: any[]; invalid: any[]; dynamic?: any };
+ payment: PaymentCreator;
+}[] = [
+ {
+ paymentName: 'embed',
+ fixtures: embedFixtures,
+ payment: p2data,
+ },
+ {
+ paymentName: 'p2ms',
+ fixtures: p2msFixtures,
+ payment: p2ms,
+ },
+ {
+ paymentName: 'p2pk',
+ fixtures: p2pkFixtures,
+ payment: p2pk,
+ },
+ {
+ paymentName: 'p2pkh',
+ fixtures: p2pkhFixtures,
+ payment: p2pkh,
+ },
+ {
+ paymentName: 'p2sh',
+ fixtures: p2shFixtures,
+ payment: p2sh,
+ },
+ {
+ paymentName: 'p2wpkh',
+ fixtures: p2wpkhFixtures,
+ payment: p2wpkh,
+ },
+ {
+ paymentName: 'p2wsh',
+ fixtures: p2wshFixtures,
+ payment: p2wsh,
+ },
+ {
+ paymentName: 'p2tr',
+ fixtures: p2trFixtures,
+ payment: p2tr,
+ },
+];
- fixtures.valid.forEach((f: any) => {
+testSuite.forEach(p => {
+ describe(p.paymentName, () => {
+ beforeEach(async () => {
+ initEccLib(p.paymentName === 'p2tr' ? ecc : undefined);
+ });
+
+ p.fixtures.valid.forEach((f: any) => {
it(f.description + ' as expected', () => {
const args = u.preform(f.arguments);
- const actual = fn(args, f.options);
+ const actual = p.payment(args, f.options);
u.equate(actual, f.expected, f.arguments);
});
it(f.description + ' as expected (no validation)', () => {
const args = u.preform(f.arguments);
- const actual = fn(
+ const actual = p.payment(
args,
Object.assign({}, f.options, {
validate: false,
@@ -34,24 +99,21 @@ import * as u from './payments.utils';
});
});
- fixtures.invalid.forEach((f: any) => {
+ p.fixtures.invalid.forEach((f: any) => {
it(
'throws ' + f.exception + (f.description ? 'for ' + f.description : ''),
() => {
const args = u.preform(f.arguments);
-
assert.throws(() => {
- fn(args, f.options);
+ p.payment(args, f.options);
}, new RegExp(f.exception));
},
);
});
- if (p === 'p2sh') {
- const p2wsh = require('../src/payments/p2wsh').p2wsh;
- const p2pk = require('../src/payments/p2pk').p2pk;
+ if (p.paymentName === 'p2sh') {
it('properly assembles nested p2wsh with names', () => {
- const actual = fn({
+ const actual = p.payment({
redeem: p2wsh({
redeem: p2pk({
pubkey: Buffer.from(
@@ -72,8 +134,8 @@ import * as u from './payments.utils';
}
// cross-verify dynamically too
- if (!fixtures.dynamic) return;
- const { depends, details } = fixtures.dynamic;
+ if (!p.fixtures!.dynamic) return;
+ const { depends, details } = p.fixtures.dynamic;
details.forEach((f: any) => {
const detail = u.preform(f);
@@ -103,7 +165,7 @@ import * as u from './payments.utils';
' derives from ' +
JSON.stringify(dependency),
() => {
- u.equate(fn(args), expected);
+ u.equate(p.payment(args), expected);
},
);
});
diff --git a/test/payments.utils.ts b/test/payments.utils.ts
index c0635f3cf..a2bde3848 100644
--- a/test/payments.utils.ts
+++ b/test/payments.utils.ts
@@ -1,23 +1,25 @@
import * as t from 'assert';
-import * as BNETWORKS from '../src/networks';
-import * as bscript from '../src/script';
+import * as BNETWORKS from 'bitcoinjs-lib/src/networks';
+import * as bscript from 'bitcoinjs-lib/src/script';
+import * as tools from 'uint8array-tools';
+import { isTaptree } from 'bitcoinjs-lib/src/types';
-function tryHex(x: Buffer | Buffer[]): string | string[] {
- if (Buffer.isBuffer(x)) return x.toString('hex');
+function tryHex(x: Uint8Array | Uint8Array[]): string | string[] {
+ if (x instanceof Uint8Array) return tools.toHex(x);
if (Array.isArray(x)) return x.map(tryHex) as string[];
return x;
}
-function fromHex(x: string | string[]): Buffer | Buffer[] {
- if (typeof x === 'string') return Buffer.from(x, 'hex');
- if (Array.isArray(x)) return x.map(fromHex) as Buffer[];
+function fromHex(x: string | string[]): Uint8Array | Uint8Array[] {
+ if (typeof x === 'string') return tools.fromHex(x);
+ if (Array.isArray(x)) return x.map(fromHex) as Uint8Array[];
return x;
}
-function tryASM(x: Buffer): string {
- if (Buffer.isBuffer(x)) return bscript.toASM(x);
+function tryASM(x: Uint8Array): string {
+ if (x instanceof Uint8Array) return bscript.toASM(x);
return x;
}
-function asmToBuffer(x: string): Buffer {
+function asmToBuffer(x: string): Uint8Array {
if (x === '') return Buffer.alloc(0);
return bscript.fromASM(x);
}
@@ -52,6 +54,12 @@ function equateBase(a: any, b: any, context: string): void {
tryHex(b.witness),
`Inequal ${context}witness`,
);
+ if ('redeemVersion' in b)
+ t.strictEqual(
+ a.redeemVersion,
+ b.redeemVersion,
+ `Inequal ${context}redeemVersion`,
+ );
}
export function equate(a: any, b: any, args?: any): void {
@@ -62,10 +70,12 @@ export function equate(a: any, b: any, args?: any): void {
if (b.input === null) b.input = undefined;
if (b.output === null) b.output = undefined;
if (b.witness === null) b.witness = undefined;
+ if (b.redeemVersion === null) b.redeemVersion = undefined;
if (b.redeem) {
if (b.redeem.input === null) b.redeem.input = undefined;
if (b.redeem.output === null) b.redeem.output = undefined;
if (b.redeem.witness === null) b.redeem.witness = undefined;
+ if (b.redeem.redeemVersion === null) b.redeem.redeemVersion = undefined;
}
equateBase(a, b, '');
@@ -86,6 +96,12 @@ export function equate(a: any, b: any, args?: any): void {
t.strictEqual(tryHex(a.hash), tryHex(b.hash), 'Inequal *.hash');
if ('pubkey' in b)
t.strictEqual(tryHex(a.pubkey), tryHex(b.pubkey), 'Inequal *.pubkey');
+ if ('internalPubkey' in b)
+ t.strictEqual(
+ tryHex(a.internalPubkey),
+ tryHex(b.internalPubkey),
+ 'Inequal *.internalPubkey',
+ );
if ('signature' in b)
t.strictEqual(
tryHex(a.signature),
@@ -129,6 +145,7 @@ export function preform(x: any): any {
if (x.data) x.data = x.data.map(fromHex);
if (x.hash) x.hash = Buffer.from(x.hash, 'hex');
if (x.pubkey) x.pubkey = Buffer.from(x.pubkey, 'hex');
+ if (x.internalPubkey) x.internalPubkey = Buffer.from(x.internalPubkey, 'hex');
if (x.signature) x.signature = Buffer.from(x.signature, 'hex');
if (x.pubkeys) x.pubkeys = x.pubkeys.map(fromHex);
if (x.signatures)
@@ -147,6 +164,7 @@ export function preform(x: any): any {
x.redeem.network = (BNETWORKS as any)[x.redeem.network];
}
+ if (x.scriptTree) x.scriptTree = convertScriptTree(x.scriptTree);
return x;
}
@@ -169,3 +187,15 @@ export function from(path: string, object: any, result?: any): any {
return result;
}
+
+export function convertScriptTree(scriptTree: any, leafVersion?: number): any {
+ if (Array.isArray(scriptTree))
+ return scriptTree.map(tr => convertScriptTree(tr, leafVersion));
+
+ const script = Object.assign({}, scriptTree);
+ if (typeof script.output === 'string') {
+ script.output = asmToBuffer(scriptTree.output);
+ script.version = script.version || leafVersion;
+ }
+ return script;
+}
diff --git a/test/psbt.spec.ts b/test/psbt.spec.ts
index 05d446878..232279b4f 100644
--- a/test/psbt.spec.ts
+++ b/test/psbt.spec.ts
@@ -1,19 +1,66 @@
import * as assert from 'assert';
-import * as bip32 from 'bip32';
+import * as BIP32Factory from 'bip32';
+import * as ecc from 'tiny-secp256k1';
import * as crypto from 'crypto';
-import { ECPair } from 'ecpair';
+import ECPairFactory from 'ecpair';
import { describe, it } from 'mocha';
-import { networks as NETWORKS, payments, Psbt, Signer, SignerAsync } from '..';
+import { convertScriptTree } from './payments.utils.js';
+import { LEAF_VERSION_TAPSCRIPT } from 'bitcoinjs-lib/src/payments/bip341';
+import { tapTreeToList, tapTreeFromList } from 'bitcoinjs-lib/src/psbt/bip371';
+import type { Taptree } from 'bitcoinjs-lib/src/types';
+import { initEccLib } from 'bitcoinjs-lib';
+import * as tools from 'uint8array-tools';
-import * as preFixtures from './fixtures/psbt.json';
+const rng = (size: number) => crypto.randomBytes(size);
+
+const bip32 = BIP32Factory.BIP32Factory(ecc);
+const ECPair = ECPairFactory(ecc);
+
+import {
+ Psbt,
+ networks as NETWORKS,
+ payments,
+ Signer,
+ SignerAsync,
+} from 'bitcoinjs-lib';
+
+import preFixtures from './fixtures/psbt.json';
+import taprootFixtures from './fixtures/p2tr.json';
const validator = (
- pubkey: Buffer,
- msghash: Buffer,
- signature: Buffer,
+ pubkey: Uint8Array,
+ msghash: Uint8Array,
+ signature: Uint8Array,
): boolean => ECPair.fromPublicKey(pubkey).verify(msghash, signature);
+function toBip174Format(data: unknown): any {
+ if (typeof data !== 'object' || data === null) {
+ return data;
+ }
+
+ if (Array.isArray(data)) {
+ return data.map(toBip174Format);
+ }
+
+ if (Buffer.isBuffer(data)) {
+ return Uint8Array.from(data);
+ }
+
+ return Object.fromEntries(
+ Object.entries(data).map(([key, value]) => [
+ key,
+ key === 'value' ? BigInt(value) : toBip174Format(value),
+ ]),
+ );
+}
+
+const schnorrValidator = (
+ pubkey: Uint8Array,
+ msghash: Uint8Array,
+ signature: Uint8Array,
+): boolean => ecc.verifySchnorr(msghash, pubkey, signature);
+
const initBuffers = (object: any): typeof preFixtures =>
JSON.parse(JSON.stringify(object), (_, value) => {
const regex = new RegExp(/^Buffer.from\(['"](.*)['"], ['"](.*)['"]\)$/);
@@ -34,40 +81,43 @@ const upperCaseFirstLetter = (str: string): string =>
const toAsyncSigner = (signer: Signer): SignerAsync => {
const ret: SignerAsync = {
publicKey: signer.publicKey,
- sign: (hash: Buffer, lowerR: boolean | undefined): Promise => {
- return new Promise(
- (resolve, rejects): void => {
- setTimeout(() => {
- try {
- const r = signer.sign(hash, lowerR);
- resolve(r);
- } catch (e) {
- rejects(e);
- }
- }, 10);
- },
- );
+ sign: (
+ hash: Uint8Array,
+ lowerR: boolean | undefined,
+ ): Promise => {
+ return new Promise((resolve, rejects): void => {
+ setTimeout(() => {
+ try {
+ const r = signer.sign(hash, lowerR);
+ resolve(r);
+ } catch (e) {
+ rejects(e);
+ }
+ }, 10);
+ });
},
};
return ret;
};
-const failedAsyncSigner = (publicKey: Buffer): SignerAsync => {
+const failedAsyncSigner = (publicKey: Uint8Array): SignerAsync => {
return {
publicKey,
- sign: (__: Buffer): Promise => {
- return new Promise(
- (_, reject): void => {
- setTimeout(() => {
- reject(new Error('sign failed'));
- }, 10);
- },
- );
+ sign: (__: Uint8Array): Promise => {
+ return new Promise((_, reject): void => {
+ setTimeout(() => {
+ reject(new Error('sign failed'));
+ }, 10);
+ });
},
};
};
// const b = (hex: string) => Buffer.from(hex, 'hex');
describe(`Psbt`, () => {
+ beforeEach(() => {
+ // provide the ECC lib only when required
+ initEccLib(undefined);
+ });
describe('BIP174 Test Vectors', () => {
fixtures.bip174.invalid.forEach(f => {
it(`Invalid: ${f.description}`, () => {
@@ -86,7 +136,7 @@ describe(`Psbt`, () => {
});
fixtures.bip174.failSignChecks.forEach(f => {
- const keyPair = ECPair.makeRandom();
+ const keyPair = ECPair.makeRandom({ rng });
it(`Fails Signer checks: ${f.description}`, () => {
const psbt = Psbt.fromBase64(f.psbt);
assert.throws(() => {
@@ -103,7 +153,7 @@ describe(`Psbt`, () => {
}
for (const output of f.outputs) {
const script = Buffer.from(output.script, 'hex');
- psbt.addOutput({ ...output, script });
+ psbt.addOutput({ value: BigInt(output.value), script });
}
assert.strictEqual(psbt.toBase64(), f.result);
});
@@ -111,6 +161,9 @@ describe(`Psbt`, () => {
fixtures.bip174.updater.forEach(f => {
it('Updates PSBT to the expected result', () => {
+ if (f.isTaproot) {
+ initEccLib(ecc);
+ }
const psbt = Psbt.fromBase64(f.psbt);
for (const inputOrOutput of ['input', 'output']) {
@@ -118,7 +171,7 @@ describe(`Psbt`, () => {
if (fixtureData) {
for (const [i, data] of fixtureData.entries()) {
const txt = upperCaseFirstLetter(inputOrOutput);
- (psbt as any)[`update${txt}`](i, data);
+ (psbt as any)[`update${txt}`](i, toBip174Format(data));
}
}
}
@@ -129,11 +182,20 @@ describe(`Psbt`, () => {
fixtures.bip174.signer.forEach(f => {
it('Signs PSBT to the expected result', () => {
+ if (f.isTaproot) initEccLib(ecc);
const psbt = Psbt.fromBase64(f.psbt);
- f.keys.forEach(({ inputToSign, WIF }) => {
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+ // @ts-ignore // cannot find tapLeafHashToSign
+ f.keys.forEach(({ inputToSign, tapLeafHashToSign, WIF }) => {
const keyPair = ECPair.fromWIF(WIF, NETWORKS.testnet);
- psbt.signInput(inputToSign, keyPair);
+ if (tapLeafHashToSign)
+ psbt.signTaprootInput(
+ inputToSign,
+ keyPair,
+ Buffer.from(tapLeafHashToSign, 'hex'),
+ );
+ else psbt.signInput(inputToSign, keyPair);
});
assert.strictEqual(psbt.toBase64(), f.result);
@@ -146,7 +208,7 @@ describe(`Psbt`, () => {
psbts[0].combine(psbts[1]);
- // Produces a different Base64 string due to implemetation specific key-value ordering.
+ // Produces a different Base64 string due to implementation specific key-value ordering.
// That means this test will fail:
// assert.strictEqual(psbts[0].toBase64(), f.result)
// However, if we compare the actual PSBT properties we can see they are logically identical:
@@ -156,6 +218,7 @@ describe(`Psbt`, () => {
fixtures.bip174.finalizer.forEach(f => {
it('Finalizes inputs and gives the expected PSBT', () => {
+ if (f.isTaproot) initEccLib(ecc);
const psbt = Psbt.fromBase64(f.psbt);
psbt.finalizeAllInputs();
@@ -205,6 +268,7 @@ describe(`Psbt`, () => {
describe('signInputAsync', () => {
fixtures.signInput.checks.forEach(f => {
it(f.description, async () => {
+ if (f.isTaproot) initEccLib(ecc);
if (f.shouldSign) {
const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt);
await assert.doesNotReject(async () => {
@@ -213,14 +277,22 @@ describe(`Psbt`, () => {
ECPair.fromWIF(f.shouldSign.WIF),
f.shouldSign.sighashTypes || undefined,
);
+ if (f.shouldSign.result)
+ assert.strictEqual(
+ psbtThatShouldsign.toBase64(),
+ f.shouldSign.result,
+ );
});
+ const failMessage = f.isTaproot
+ ? /Need Schnorr Signer to sign taproot input #0./
+ : /sign failed/;
await assert.rejects(async () => {
await psbtThatShouldsign.signInputAsync(
f.shouldSign.inputToCheck,
failedAsyncSigner(ECPair.fromWIF(f.shouldSign.WIF).publicKey),
f.shouldSign.sighashTypes || undefined,
);
- }, /sign failed/);
+ }, failMessage);
}
if (f.shouldThrow) {
@@ -252,6 +324,7 @@ describe(`Psbt`, () => {
describe('signInput', () => {
fixtures.signInput.checks.forEach(f => {
it(f.description, () => {
+ if (f.isTaproot) initEccLib(ecc);
if (f.shouldSign) {
const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt);
assert.doesNotThrow(() => {
@@ -284,6 +357,7 @@ describe(`Psbt`, () => {
fixtures.signInput.checks.forEach(f => {
if (f.description === 'checks the input exists') return;
it(f.description, async () => {
+ if (f.isTaproot) initEccLib(ecc);
if (f.shouldSign) {
const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt);
await assert.doesNotReject(async () => {
@@ -314,6 +388,7 @@ describe(`Psbt`, () => {
fixtures.signInput.checks.forEach(f => {
if (f.description === 'checks the input exists') return;
it(f.description, () => {
+ if (f.isTaproot) initEccLib(ecc);
if (f.shouldSign) {
const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt);
assert.doesNotThrow(() => {
@@ -464,6 +539,41 @@ describe(`Psbt`, () => {
});
});
+ describe('finalizeInput', () => {
+ it(`Finalizes tapleaf by hash`, () => {
+ const f = fixtures.finalizeInput.finalizeTapleafByHash;
+ const psbt = Psbt.fromBase64(f.psbt);
+
+ psbt.finalizeTaprootInput(f.index, Buffer.from(f.leafHash, 'hex'));
+
+ assert.strictEqual(psbt.toBase64(), f.result);
+ });
+
+ it(`fails if tapleaf hash not found`, () => {
+ const f = fixtures.finalizeInput.finalizeTapleafByHash;
+ const psbt = Psbt.fromBase64(f.psbt);
+
+ assert.throws(() => {
+ psbt.finalizeTaprootInput(
+ f.index,
+ Buffer.from(f.leafHash, 'hex').reverse(),
+ );
+ }, new RegExp('Can not finalize taproot input #0. Signature for tapleaf script not found.'));
+ });
+
+ it(`fails if trying to finalzie non-taproot input`, () => {
+ const psbt = new Psbt();
+ psbt.addInput({
+ hash: '0000000000000000000000000000000000000000000000000000000000000000',
+ index: 0,
+ });
+
+ assert.throws(() => {
+ psbt.finalizeTaprootInput(0);
+ }, new RegExp('Cannot finalize input #0. Not Taproot.'));
+ });
+ });
+
describe('finalizeAllInputs', () => {
fixtures.finalizeAllInputs.forEach(f => {
it(`Finalizes inputs of type "${f.type}"`, () => {
@@ -477,8 +587,7 @@ describe(`Psbt`, () => {
it('fails if no script found', () => {
const psbt = new Psbt();
psbt.addInput({
- hash:
- '0000000000000000000000000000000000000000000000000000000000000000',
+ hash: '0000000000000000000000000000000000000000000000000000000000000000',
index: 0,
});
assert.throws(() => {
@@ -486,11 +595,8 @@ describe(`Psbt`, () => {
}, new RegExp('No script found for input #0'));
psbt.updateInput(0, {
witnessUtxo: {
- script: Buffer.from(
- '0014d85c2b71d0060b09c9886aeb815e50991dda124d',
- 'hex',
- ),
- value: 2e5,
+ script: tools.fromHex('0014d85c2b71d0060b09c9886aeb815e50991dda124d'),
+ value: BigInt(2e5),
},
});
assert.throws(() => {
@@ -526,10 +632,25 @@ describe(`Psbt`, () => {
});
});
+ describe('updateInput', () => {
+ fixtures.updateInput.checks.forEach(f => {
+ it(f.description, () => {
+ const psbt = Psbt.fromBase64(f.psbt);
+
+ if (f.exception) {
+ assert.throws(() => {
+ psbt.updateInput(f.index, f.inputData as any);
+ }, new RegExp(f.exception));
+ }
+ });
+ });
+ });
+
describe('addOutput', () => {
fixtures.addOutput.checks.forEach(f => {
it(f.description, () => {
- const psbt = new Psbt();
+ if (f.isTaproot) initEccLib(ecc);
+ const psbt = f.psbt ? Psbt.fromBase64(f.psbt) : new Psbt();
if (f.exception) {
assert.throws(() => {
@@ -540,10 +661,13 @@ describe(`Psbt`, () => {
}, new RegExp(f.exception));
} else {
assert.doesNotThrow(() => {
- psbt.addOutput(f.outputData as any);
+ psbt.addOutput(toBip174Format(f.outputData));
});
+ if (f.result) {
+ assert.strictEqual(psbt.toBase64(), f.result);
+ }
assert.doesNotThrow(() => {
- psbt.addOutputs([f.outputData as any]);
+ psbt.addOutputs([toBip174Format(f.outputData)]);
});
}
});
@@ -574,8 +698,7 @@ describe(`Psbt`, () => {
it('Sets the sequence number for a given input', () => {
const psbt = new Psbt();
psbt.addInput({
- hash:
- '0000000000000000000000000000000000000000000000000000000000000000',
+ hash: '0000000000000000000000000000000000000000000000000000000000000000',
index: 0,
});
@@ -588,8 +711,7 @@ describe(`Psbt`, () => {
it('throws if input index is too high', () => {
const psbt = new Psbt();
psbt.addInput({
- hash:
- '0000000000000000000000000000000000000000000000000000000000000000',
+ hash: '0000000000000000000000000000000000000000000000000000000000000000',
index: 0,
});
@@ -600,26 +722,27 @@ describe(`Psbt`, () => {
});
describe('getInputType', () => {
- const key = ECPair.makeRandom();
+ const key = ECPair.makeRandom({ rng });
const { publicKey } = key;
- const p2wpkhPub = (pubkey: Buffer): Buffer =>
+ const p2wpkhPub = (pubkey: Uint8Array): Uint8Array =>
payments.p2wpkh({
pubkey,
}).output!;
- const p2pkhPub = (pubkey: Buffer): Buffer =>
+ const p2pkhPub = (pubkey: Uint8Array): Uint8Array =>
payments.p2pkh({
pubkey,
}).output!;
- const p2shOut = (output: Buffer): Buffer =>
+ const p2shOut = (output: Uint8Array): Uint8Array =>
payments.p2sh({
redeem: { output },
}).output!;
- const p2wshOut = (output: Buffer): Buffer =>
+ const p2wshOut = (output: Uint8Array): Uint8Array =>
payments.p2wsh({
redeem: { output },
}).output!;
- const p2shp2wshOut = (output: Buffer): Buffer => p2shOut(p2wshOut(output));
- const noOuter = (output: Buffer): Buffer => output;
+ const p2shp2wshOut = (output: Uint8Array): Uint8Array =>
+ p2shOut(p2wshOut(output));
+ const noOuter = (output: Uint8Array): Uint8Array => output;
function getInputTypeTest({
innerScript,
@@ -632,19 +755,18 @@ describe(`Psbt`, () => {
const psbt = new Psbt();
psbt
.addInput({
- hash:
- '0000000000000000000000000000000000000000000000000000000000000000',
+ hash: '0000000000000000000000000000000000000000000000000000000000000000',
index: 0,
witnessUtxo: {
script: outerScript(innerScript(publicKey)),
- value: 2e3,
+ value: BigInt(2e3),
},
...(redeemGetter ? { redeemScript: redeemGetter(publicKey) } : {}),
...(witnessGetter ? { witnessScript: witnessGetter(publicKey) } : {}),
})
.addOutput({
script: Buffer.from('0014d85c2b71d0060b09c9886aeb815e50991dda124d'),
- value: 1800,
+ value: BigInt(1800),
});
if (finalize) psbt.signInput(0, key).finalizeInput(0);
const type = psbt.getInputType(0);
@@ -691,7 +813,7 @@ describe(`Psbt`, () => {
{
innerScript: p2pkhPub,
outerScript: p2shp2wshOut,
- redeemGetter: (pk: Buffer): Buffer => p2wshOut(p2pkhPub(pk)),
+ redeemGetter: (pk: Uint8Array): Uint8Array => p2wshOut(p2pkhPub(pk)),
witnessGetter: p2pkhPub,
expectedType: 'p2sh-p2wsh-pubkeyhash',
},
@@ -705,8 +827,7 @@ describe(`Psbt`, () => {
const path = "m/0'/0";
const psbt = new Psbt();
psbt.addInput({
- hash:
- '0000000000000000000000000000000000000000000000000000000000000000',
+ hash: '0000000000000000000000000000000000000000000000000000000000000000',
index: 0,
bip32Derivation: [
{
@@ -725,8 +846,7 @@ describe(`Psbt`, () => {
it('should throw', () => {
const psbt = new Psbt();
psbt.addInput({
- hash:
- '0000000000000000000000000000000000000000000000000000000000000000',
+ hash: '0000000000000000000000000000000000000000000000000000000000000000',
index: 0,
});
@@ -736,7 +856,7 @@ describe(`Psbt`, () => {
psbt.updateInput(0, {
witnessUtxo: {
- value: 1337,
+ value: 1337n,
script: payments.p2sh({
redeem: { output: Buffer.from([0x51]) },
}).output!,
@@ -751,7 +871,7 @@ describe(`Psbt`, () => {
psbt.updateInput(0, {
witnessUtxo: {
- value: 1337,
+ value: 1337n,
script: payments.p2wsh({
redeem: { output: Buffer.from([0x51]) },
}).output!,
@@ -766,7 +886,7 @@ describe(`Psbt`, () => {
psbt.updateInput(0, {
witnessUtxo: {
- value: 1337,
+ value: 1337n,
script: payments.p2sh({
redeem: payments.p2wsh({
redeem: { output: Buffer.from([0x51]) },
@@ -800,8 +920,7 @@ describe(`Psbt`, () => {
const psbt = new Psbt();
psbt
.addInput({
- hash:
- '0000000000000000000000000000000000000000000000000000000000000000',
+ hash: '0000000000000000000000000000000000000000000000000000000000000000',
index: 0,
})
.addOutput({
@@ -809,7 +928,7 @@ describe(`Psbt`, () => {
'0014000102030405060708090a0b0c0d0e0f00010203',
'hex',
),
- value: 2000,
+ value: 2000n,
bip32Derivation: [
{
masterFingerprint: root.fingerprint,
@@ -828,15 +947,14 @@ describe(`Psbt`, () => {
const psbt = new Psbt();
psbt
.addInput({
- hash:
- '0000000000000000000000000000000000000000000000000000000000000000',
+ hash: '0000000000000000000000000000000000000000000000000000000000000000',
index: 0,
})
.addOutput({
script: payments.p2sh({
redeem: { output: Buffer.from([0x51]) },
}).output!,
- value: 1337,
+ value: 1337n,
});
assert.throws(() => {
@@ -948,6 +1066,135 @@ describe(`Psbt`, () => {
});
});
+ describe('validateSignaturesOfTapKeyInput', () => {
+ const f = fixtures.validateSignaturesOfTapKeyInput;
+ it('Correctly validates all signatures', () => {
+ initEccLib(ecc);
+ const psbt = Psbt.fromBase64(f.psbt);
+ assert.strictEqual(
+ psbt.validateSignaturesOfInput(f.index, schnorrValidator),
+ true,
+ );
+ });
+
+ it('Correctly validates a signature against a pubkey', () => {
+ initEccLib(ecc);
+ const psbt = Psbt.fromBase64(f.psbt);
+ assert.strictEqual(
+ psbt.validateSignaturesOfInput(
+ f.index,
+ schnorrValidator,
+ f.pubkey as any,
+ ),
+ true,
+ );
+ assert.throws(() => {
+ psbt.validateSignaturesOfInput(
+ f.index,
+ schnorrValidator,
+ f.incorrectPubkey as any,
+ );
+ }, new RegExp('No signatures for this pubkey'));
+ });
+ });
+
+ describe('validateSignaturesOfTapScriptInput', () => {
+ const f = fixtures.validateSignaturesOfTapScriptInput;
+ it('Correctly validates all signatures', () => {
+ initEccLib(ecc);
+ const psbt = Psbt.fromBase64(f.psbt);
+ assert.strictEqual(
+ psbt.validateSignaturesOfInput(f.index, schnorrValidator),
+ true,
+ );
+ });
+
+ it('Correctly validates a signature against a pubkey', () => {
+ initEccLib(ecc);
+ const psbt = Psbt.fromBase64(f.psbt);
+ assert.strictEqual(
+ psbt.validateSignaturesOfInput(
+ f.index,
+ schnorrValidator,
+ f.pubkey as any,
+ ),
+ true,
+ );
+ assert.throws(() => {
+ psbt.validateSignaturesOfInput(
+ f.index,
+ schnorrValidator,
+ f.incorrectPubkey as any,
+ );
+ }, new RegExp('No signatures for this pubkey'));
+ });
+ });
+
+ describe('tapTreeToList/tapTreeFromList', () => {
+ it('Correctly converts a Taptree to a Tapleaf list and back', () => {
+ taprootFixtures.valid
+ .filter(f => f.arguments.scriptTree)
+ .map(f => f.arguments.scriptTree)
+ .forEach(scriptTree => {
+ const originalTree = convertScriptTree(
+ scriptTree,
+ LEAF_VERSION_TAPSCRIPT,
+ );
+ const list = tapTreeToList(originalTree);
+ const treeFromList = tapTreeFromList(list);
+
+ assert.deepStrictEqual(treeFromList, originalTree);
+ });
+ });
+
+ it('Throws if too many leaves on a given level', () => {
+ const list = Array.from({ length: 5 }).map(() => ({
+ depth: 2,
+ leafVersion: LEAF_VERSION_TAPSCRIPT,
+ script: Buffer.from([]),
+ }));
+ assert.throws(() => {
+ tapTreeFromList(list);
+ }, new RegExp('No room left to insert tapleaf in tree'));
+ });
+
+ it('Throws if taptree depth is exceeded', () => {
+ let tree: Taptree = [
+ { output: Buffer.from([]) },
+ { output: Buffer.from([]) },
+ ];
+ Array.from({ length: 129 }).forEach(
+ () => (tree = [tree, { output: Buffer.from([]) }]),
+ );
+ assert.throws(() => {
+ tapTreeToList(tree as Taptree);
+ }, new RegExp('Max taptree depth exceeded.'));
+ });
+
+ it('Throws if tapleaf depth is to high', () => {
+ const list = [
+ {
+ depth: 129,
+ leafVersion: LEAF_VERSION_TAPSCRIPT,
+ script: Buffer.from([]),
+ },
+ ];
+ assert.throws(() => {
+ tapTreeFromList(list);
+ }, new RegExp('Max taptree depth exceeded.'));
+ });
+
+ it('Throws if not a valid taptree structure', () => {
+ const tree = Array.from({ length: 3 }).map(() => ({
+ output: Buffer.from([]),
+ }));
+
+ assert.throws(() => {
+ tapTreeToList(tree as unknown as Taptree);
+ }, new RegExp('Cannot convert taptree to tapleaf list. Expecting a tapree structure.'));
+ });
+ });
+
describe('getFeeRate', () => {
it('Throws error if called before inputs are finalized', () => {
const f = fixtures.getFeeRate;
@@ -986,7 +1233,7 @@ describe(`Psbt`, () => {
});
psbt.addOutput({
address: '1KRMKfeZcmosxALVYESdPNez1AP1mEtywp',
- value: 80000,
+ value: 80000n,
});
psbt.signInput(0, alice);
assert.throws(() => {
@@ -1060,7 +1307,12 @@ describe(`Psbt`, () => {
// Cache is rebuilt from internal transaction object when cleared
psbt.data.inputs[index].nonWitnessUtxo = Buffer.from([1, 2, 3]);
(psbt as any).__CACHE.__NON_WITNESS_UTXO_BUF_CACHE[index] = undefined;
- assert.ok((psbt as any).data.inputs[index].nonWitnessUtxo.equals(value));
+ assert.ok(
+ tools.compare(
+ (psbt as any).data.inputs[index].nonWitnessUtxo,
+ value!,
+ ) === 0,
+ );
});
});
@@ -1096,7 +1348,7 @@ describe(`Psbt`, () => {
const input = psbt.txInputs[0];
const internalInput = (psbt as any).__CACHE.__TX.ins[0];
- assert.ok(input.hash.equals(internalInput.hash));
+ assert.ok(tools.compare(input.hash, internalInput.hash) === 0);
assert.strictEqual(input.index, internalInput.index);
assert.strictEqual(input.sequence, internalInput.sequence);
@@ -1104,7 +1356,7 @@ describe(`Psbt`, () => {
input.index = 123;
input.sequence = 123;
- assert.ok(!input.hash.equals(internalInput.hash));
+ assert.ok(tools.compare(input.hash, internalInput.hash) !== 0);
assert.notEqual(input.index, internalInput.index);
assert.notEqual(input.sequence, internalInput.sequence);
});
@@ -1112,7 +1364,7 @@ describe(`Psbt`, () => {
it('.txOutputs is exposed as a readonly clone', () => {
const psbt = new Psbt();
const address = '1LukeQU5jwebXbMLDVydeH4vFSobRV9rkj';
- const value = 100000;
+ const value = 100000n;
psbt.addOutput({ address, value });
const output = psbt.txOutputs[0];
@@ -1120,13 +1372,13 @@ describe(`Psbt`, () => {
assert.strictEqual(output.address, address);
- assert.ok(output.script.equals(internalInput.script));
+ assert.ok(tools.compare(output.script, internalInput.script) === 0);
assert.strictEqual(output.value, internalInput.value);
output.script[0] = 123;
- output.value = 123;
+ output.value = 123n;
- assert.ok(!output.script.equals(internalInput.script));
+ assert.ok(tools.compare(output.script, internalInput.script) !== 0);
assert.notEqual(output.value, internalInput.value);
});
});
diff --git a/test/script.spec.ts b/test/script.spec.ts
index d593ab17d..3736cdbdf 100644
--- a/test/script.spec.ts
+++ b/test/script.spec.ts
@@ -1,9 +1,10 @@
import * as assert from 'assert';
import { describe, it } from 'mocha';
-import * as bscript from '../src/script';
-import * as fixtures from './fixtures/script.json';
-const minimalData = require('minimaldata');
+import { script as bscript } from 'bitcoinjs-lib';
+import fixtures from './fixtures/script.json';
+import minimalData from 'minimaldata';
+import * as tools from 'uint8array-tools';
describe('script', () => {
// TODO
describe('isCanonicalPubKey', () => {
@@ -40,6 +41,27 @@ describe('script', () => {
});
});
+ describe('fromASM', () => {
+ const OPS = bscript.OPS;
+ it('decodes OP_FALSE as empty buffer', () => {
+ const string = 'OP_RETURN OP_FALSE';
+ assert.deepStrictEqual(
+ bscript.fromASM(string),
+ Uint8Array.from([OPS.OP_RETURN, OPS.OP_FALSE]),
+ );
+ });
+
+ it("decodes a series of numbers from '82 to 96' correctly", () => {
+ const asm = Array.from({ length: 15 }, (_, i) => i + 82).join(' ');
+ const expected = Array.from({ length: 15 }, (_, i) => [
+ 1,
+ parseInt(String(i + 82), 16),
+ ]).flat();
+ const result = bscript.fromASM(asm);
+ assert.deepStrictEqual(result, Uint8Array.from(expected));
+ });
+ });
+
describe('toASM', () => {
const OP_RETURN = bscript.OPS.OP_RETURN;
it('encodes empty buffer as OP_0', () => {
@@ -62,7 +84,7 @@ describe('script', () => {
it('encodes/decodes ' + ih, () => {
const script = bscript.fromASM(f.input);
- assert.strictEqual(script.toString('hex'), f.inputHex);
+ assert.strictEqual(tools.toHex(script), f.inputHex);
assert.strictEqual(bscript.toASM(script), f.input);
});
}
@@ -70,7 +92,7 @@ describe('script', () => {
if (f.outputHex) {
it('encodes/decodes ' + f.output, () => {
const script = bscript.fromASM(f.output);
- assert.strictEqual(script.toString('hex'), f.outputHex);
+ assert.strictEqual(tools.toHex(script), f.outputHex);
assert.strictEqual(bscript.toASM(script), f.output);
});
}
@@ -89,7 +111,7 @@ describe('script', () => {
});
describe('toStack', () => {
- fixtures.valid.forEach(f => {
+ fixtures.valid.forEach((f, i) => {
it('returns ' + !!f.stack + ' for ' + f.asm, () => {
if (!f.stack || !f.asm) return;
@@ -98,7 +120,7 @@ describe('script', () => {
const stack = bscript.toStack(script);
assert.deepStrictEqual(
stack.map(x => {
- return x.toString('hex');
+ return tools.toHex(x);
}),
f.stack,
);
@@ -117,12 +139,12 @@ describe('script', () => {
it('compiles ' + f.asm, () => {
const scriptSig = bscript.fromASM(f.asm);
- assert.strictEqual(scriptSig.toString('hex'), f.script);
+ assert.strictEqual(tools.toHex(scriptSig), f.script);
if (f.nonstandard) {
const scriptSigNS = bscript.fromASM(f.nonstandard.scriptSig);
- assert.strictEqual(scriptSigNS.toString('hex'), f.script);
+ assert.strictEqual(tools.toHex(scriptSigNS), f.script);
}
});
});
@@ -133,7 +155,7 @@ describe('script', () => {
it('decompiles ' + f.asm, () => {
const chunks = bscript.decompile(Buffer.from(f.script, 'hex'));
- assert.strictEqual(bscript.compile(chunks!).toString('hex'), f.script);
+ assert.strictEqual(tools.toHex(bscript.compile(chunks!)), f.script);
assert.strictEqual(bscript.toASM(chunks!), f.asm);
if (f.nonstandard) {
@@ -141,10 +163,7 @@ describe('script', () => {
Buffer.from(f.nonstandard.scriptSigHex, 'hex'),
);
- assert.strictEqual(
- bscript.compile(chunksNS!).toString('hex'),
- f.script,
- );
+ assert.strictEqual(tools.toHex(bscript.compile(chunksNS!)), f.script);
// toASM converts verbatim, only `compile` transforms the script to a minimalpush compliant script
assert.strictEqual(bscript.toASM(chunksNS!), f.nonstandard.scriptSig);
@@ -169,7 +188,7 @@ describe('script', () => {
it('compliant for scriptSig ' + f.asm, () => {
const script = Buffer.from(f.script, 'hex');
- assert(minimalData(script));
+ assert.equal(minimalData(script), true);
});
});
@@ -178,9 +197,10 @@ describe('script', () => {
const buffer = Buffer.alloc(num);
const script = bscript.compile([buffer]);
- assert(
- minimalData(script),
- 'Failed for ' + num + ' length script: ' + script.toString('hex'),
+ assert.equal(
+ minimalData(Buffer.from(script)),
+ true,
+ 'Failed for ' + num + ' length script: ' + tools.toHex(script),
);
});
}
diff --git a/test/script_number.spec.ts b/test/script_number.spec.ts
index 3b05082fb..e0082f1fd 100644
--- a/test/script_number.spec.ts
+++ b/test/script_number.spec.ts
@@ -1,7 +1,8 @@
import * as assert from 'assert';
import { describe, it } from 'mocha';
-import * as scriptNumber from '../src/script_number';
-import * as fixtures from './fixtures/script_number.json';
+import * as scriptNumber from 'bitcoinjs-lib/src/script_number';
+import fixtures from './fixtures/script_number.json';
+import * as tools from 'uint8array-tools';
describe('script-number', () => {
describe('decode', () => {
@@ -19,7 +20,7 @@ describe('script-number', () => {
it(f.number + ' returns ' + f.hex, () => {
const actual = scriptNumber.encode(f.number);
- assert.strictEqual(actual.toString('hex'), f.hex);
+ assert.strictEqual(tools.toHex(actual), f.hex);
});
});
});
diff --git a/test/script_signature.spec.ts b/test/script_signature.spec.ts
index 54c416e35..c798a053e 100644
--- a/test/script_signature.spec.ts
+++ b/test/script_signature.spec.ts
@@ -1,25 +1,25 @@
import * as assert from 'assert';
import { describe, it } from 'mocha';
-import { signature as bscriptSig } from '../src/script';
-import * as fixtures from './fixtures/signature.json';
+import { script } from 'bitcoinjs-lib';
+const bscriptSig = script.signature;
+import fixtures from './fixtures/signature.json';
+import * as tools from 'uint8array-tools';
describe('Script Signatures', () => {
- function fromRaw(signature: { r: string; s: string }): Buffer {
- return Buffer.concat(
- [Buffer.from(signature.r, 'hex'), Buffer.from(signature.s, 'hex')],
- 64,
- );
+ function fromRaw(signature: { r: string; s: string }): Uint8Array {
+ return tools.concat([
+ tools.fromHex(signature.r),
+ tools.fromHex(signature.s),
+ ]);
}
- function toRaw(
- signature: Buffer,
- ): {
+ function toRaw(signature: Uint8Array): {
r: string;
s: string;
} {
return {
- r: signature.slice(0, 32).toString('hex'),
- s: signature.slice(32, 64).toString('hex'),
+ r: tools.toHex(signature.subarray(0, 32)),
+ s: tools.toHex(signature.subarray(32, 64)),
};
}
@@ -28,7 +28,7 @@ describe('Script Signatures', () => {
it('encodes ' + f.hex, () => {
const buffer = bscriptSig.encode(fromRaw(f.raw), f.hashType);
- assert.strictEqual(buffer.toString('hex'), f.hex);
+ assert.strictEqual(tools.toHex(buffer), f.hex);
});
});
diff --git a/test/transaction.spec.ts b/test/transaction.spec.ts
index 13d64d128..741c72a59 100644
--- a/test/transaction.spec.ts
+++ b/test/transaction.spec.ts
@@ -1,8 +1,8 @@
import * as assert from 'assert';
import { beforeEach, describe, it } from 'mocha';
-import { Transaction } from '..';
-import * as bscript from '../src/script';
-import * as fixtures from './fixtures/transaction.json';
+import { Transaction, script as bscript } from 'bitcoinjs-lib';
+import fixtures from './fixtures/transaction.json';
+import * as tools from 'uint8array-tools';
describe('Transaction', () => {
function fromRaw(raw: any, noWitness?: boolean): Transaction {
@@ -32,7 +32,7 @@ describe('Transaction', () => {
});
raw.outs.forEach((txOut: any) => {
- let script: Buffer;
+ let script: Uint8Array;
if (txOut.data) {
script = Buffer.from(txOut.data, 'hex');
@@ -40,7 +40,7 @@ describe('Transaction', () => {
script = bscript.fromASM(txOut.script);
}
- tx.addOutput(script!, txOut.value);
+ tx.addOutput(script!, BigInt(txOut.value));
});
return tx;
@@ -78,10 +78,10 @@ describe('Transaction', () => {
});
});
- it('.version should be interpreted as an int32le', () => {
+ it('.version should be interpreted as an uint32le', () => {
const txHex = 'ffffffff0000ffffffff';
const tx = Transaction.fromHex(txHex);
- assert.strictEqual(-1, tx.version);
+ assert.strictEqual(0xffffffff, tx.version);
assert.strictEqual(0xffffffff, tx.locktime);
});
});
@@ -112,8 +112,8 @@ describe('Transaction', () => {
assert.strictEqual(a.length, byteLength);
assert.strictEqual(b.length, byteLength);
- assert.strictEqual(a.toString('hex'), f.hex);
- assert.strictEqual(b.toString('hex'), f.hex);
+ assert.strictEqual(tools.toHex(a), f.hex);
+ assert.strictEqual(tools.toHex(b), f.hex);
assert.deepStrictEqual(a, b);
assert.deepStrictEqual(a, target.slice(0, byteLength));
assert.deepStrictEqual(b, target.slice(byteLength));
@@ -135,6 +135,16 @@ describe('Transaction', () => {
});
});
+ describe('stripWitnesses', () => {
+ fixtures.valid.forEach(f => {
+ it('removes witness from the transaction if it exists', () => {
+ const T = Transaction.fromHex(f.whex ? f.whex : f.hex);
+ T.stripWitnesses();
+ assert.strictEqual(T.hasWitnesses(), false);
+ });
+ });
+ });
+
describe('weight/virtualSize', () => {
it('computes virtual size', () => {
fixtures.valid.forEach(f => {
@@ -192,8 +202,8 @@ describe('Transaction', () => {
describe('addOutput', () => {
it('returns an index', () => {
const tx = new Transaction();
- assert.strictEqual(tx.addOutput(Buffer.alloc(0), 0), 0);
- assert.strictEqual(tx.addOutput(Buffer.alloc(0), 0), 1);
+ assert.strictEqual(tx.addOutput(Buffer.alloc(0), 0n), 0);
+ assert.strictEqual(tx.addOutput(Buffer.alloc(0), 0n), 1);
});
});
@@ -222,7 +232,7 @@ describe('Transaction', () => {
it('should return the id for ' + f.id + '(' + f.description + ')', () => {
const tx = Transaction.fromHex(f.whex || f.hex);
- assert.strictEqual(tx.getHash().toString('hex'), f.hash);
+ assert.strictEqual(tools.toHex(tx.getHash()), f.hash);
assert.strictEqual(tx.getId(), f.id);
});
}
@@ -263,10 +273,10 @@ describe('Transaction', () => {
),
0,
);
- tx.addOutput(randScript, 5000000000);
+ tx.addOutput(randScript, 5000000000n);
const original = (tx as any).__toBuffer;
- (tx as any).__toBuffer = function(
+ (tx as any).__toBuffer = function (
this: Transaction,
a: any,
b: any,
@@ -298,7 +308,7 @@ describe('Transaction', () => {
const script = bscript.fromASM(f.script);
assert.strictEqual(
- tx.hashForSignature(f.inIndex, script, f.type).toString('hex'),
+ tools.toHex(tx.hashForSignature(f.inIndex, script, f.type)),
f.hash,
);
},
@@ -318,9 +328,9 @@ describe('Transaction', () => {
const script = bscript.fromASM(f.script);
assert.strictEqual(
- tx
- .hashForWitnessV0(f.inIndex, script, f.value, f.type)
- .toString('hex'),
+ tools.toHex(
+ tx.hashForWitnessV0(f.inIndex, script, BigInt(f.value), f.type),
+ ),
f.hash,
);
},
@@ -334,26 +344,26 @@ describe('Transaction', () => {
const prevOutScripts = f.utxos.map(({ scriptHex }) =>
Buffer.from(scriptHex, 'hex'),
);
- const values = f.utxos.map(({ value }) => value);
+ const values = f.utxos.map(({ value }) => BigInt(value));
f.cases.forEach(c => {
- let hash: Buffer;
+ let hash: Uint8Array;
it(`should hash to ${c.hash} for ${f.description}:${c.vin}`, () => {
const hashType = Buffer.from(c.typeHex, 'hex').readUInt8(0);
hash = tx.hashForWitnessV1(c.vin, prevOutScripts, values, hashType);
- assert.strictEqual(hash.toString('hex'), c.hash);
+ assert.strictEqual(tools.toHex(hash), c.hash);
});
});
});
});
describe('setWitness', () => {
- it('only accepts a a witness stack (Array of Buffers)', () => {
+ it('only accepts a witness stack (Array of Buffers)', () => {
assert.throws(() => {
(new Transaction().setWitness as any)(0, 'foobar');
- }, /Expected property "1" of type \[Buffer], got String "foobar"/);
+ }, /ValiError: Invalid type: Expected Array but received "foobar"/);
});
});
});
diff --git a/test/ts-node-register.js b/test/ts-node-register.js
deleted file mode 100644
index fef284dc2..000000000
--- a/test/ts-node-register.js
+++ /dev/null
@@ -1,5 +0,0 @@
-// This file is required to run mocha tests on the TS files directly
-
-require("ts-node").register({
- project: "test/tsconfig.json",
-});
diff --git a/test/tsconfig.json b/test/tsconfig.json
index 6752ec5f3..f416a161d 100644
--- a/test/tsconfig.json
+++ b/test/tsconfig.json
@@ -1,40 +1,109 @@
{
"compilerOptions": {
- "target": "ES2020",
- "module": "commonjs",
- "outDir": "../",
- "declaration": false,
- "rootDir": "../",
- "rootDirs": [
- "../src",
- "../types"
- ],
- "types": [
- "node",
- "mocha"
- ],
- "allowJs": false,
- "resolveJsonModule": true,
- "strict": true,
- "noImplicitAny": true,
- "strictNullChecks": true,
- "strictFunctionTypes": true,
- "strictBindCallApply": true,
- "strictPropertyInitialization": true,
- "noImplicitThis": true,
- "alwaysStrict": true,
- "esModuleInterop": false,
- "noUnusedLocals": true,
- "noUnusedParameters": true,
- "baseUrl": ".",
- "paths": {
- "../src/*": ["../ts_src/*"]
- }
- },
- "include": [
- "./**/*.ts"
- ],
- "exclude": [
- "../ts_src/**/*.ts"
- ]
+ /* Visit https://aka.ms/tsconfig to read more about this file */
+
+ /* Projects */
+ // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
+ // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
+ // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
+ // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
+ // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
+ // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
+
+ /* Language and Environment */
+ "target": "ESNext", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
+ // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
+ // "jsx": "preserve", /* Specify what JSX code is generated. */
+ // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
+ // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
+ // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
+ // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
+ // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
+ // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
+ // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
+ // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
+ // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
+
+ /* Modules */
+ "module": "NodeNext", /* Specify what module code is generated. */
+ "rootDir": "./", /* Specify the root folder within your source files. */
+ "moduleResolution": "NodeNext", /* Specify how TypeScript looks up a file from a given module specifier. */
+ "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
+ // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
+ // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
+ // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
+ // "types": [], /* Specify type package names to be included without being referenced in a source file. */
+ // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
+ // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
+ // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */
+ // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
+ // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
+ // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
+ "resolveJsonModule": true, /* Enable importing .json files. */
+ // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
+ // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */
+
+ /* JavaScript Support */
+ // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
+ // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
+ // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
+
+ /* Emit */
+ // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
+ // "declarationMap": true, /* Create sourcemaps for d.ts files. */
+ // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
+ // "sourceMap": true, /* Create source map files for emitted JavaScript files. */
+ // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
+ // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
+ // "outDir": "./", /* Specify an output folder for all emitted files. */
+ // "removeComments": true, /* Disable emitting comments. */
+ // "noEmit": true, /* Disable emitting files from a compilation. */
+ // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
+ // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
+ // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
+ // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
+ // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
+ // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
+ // "newLine": "crlf", /* Set the newline character for emitting files. */
+ // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
+ // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
+ // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
+ // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
+ // "declarationDir": "./", /* Specify the output directory for generated declaration files. */
+
+ /* Interop Constraints */
+ // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
+ // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
+ // "isolatedDeclarations": true, /* Require sufficient annotation on exports so other tools can trivially generate declaration files. */
+ // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
+ "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
+ // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
+ "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
+
+ /* Type Checking */
+ "strict": true, /* Enable all strict type-checking options. */
+ // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
+ // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
+ // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
+ // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
+ // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
+ // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
+ // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
+ // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
+ // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
+ // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
+ // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
+ // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
+ // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
+ // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
+ // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
+ // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
+ // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
+ // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
+
+ /* Completeness */
+ // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
+ "skipLibCheck": true /* Skip type checking all .d.ts files. */
+ },
+ "include": ["**/*.ts",]
}
diff --git a/test/types.spec.ts b/test/types.spec.ts
index 478fd997e..e258fa271 100644
--- a/test/types.spec.ts
+++ b/test/types.spec.ts
@@ -1,7 +1,7 @@
import * as assert from 'assert';
import { describe, it } from 'mocha';
-import * as types from '../src/types';
-const typeforce = require('typeforce');
+import * as types from 'bitcoinjs-lib/src/types';
+import * as v from 'valibot';
describe('types', () => {
describe('Buffer Hash160/Hash256', () => {
@@ -9,21 +9,21 @@ describe('types', () => {
const buffer32byte = Buffer.alloc(32);
it('return true for valid size', () => {
- assert(types.Hash160bit(buffer20byte));
- assert(types.Hash256bit(buffer32byte));
+ assert.equal(v.is(types.Hash160bitSchema, buffer20byte), true);
+ assert.equal(v.is(types.Hash256bitSchema, buffer32byte), true);
});
it('return true for oneOf', () => {
assert.doesNotThrow(() => {
- typeforce(
- types.oneOf(types.Hash160bit, types.Hash256bit),
+ v.parse(
+ v.union([types.Hash160bitSchema, types.Hash256bitSchema]),
buffer32byte,
);
});
assert.doesNotThrow(() => {
- typeforce(
- types.oneOf(types.Hash256bit, types.Hash160bit),
+ v.parse(
+ v.union([types.Hash256bitSchema, types.Hash160bitSchema]),
buffer32byte,
);
});
@@ -31,64 +31,29 @@ describe('types', () => {
it('throws for invalid size', () => {
assert.throws(() => {
- types.Hash160bit(buffer32byte);
- }, /Expected Buffer\(Length: 20\), got Buffer\(Length: 32\)/);
+ v.parse(types.Hash160bitSchema, buffer32byte);
+ }, /ValiError: Invalid length: Expected 20 but received 32/);
assert.throws(() => {
- types.Hash256bit(buffer20byte);
- }, /Expected Buffer\(Length: 32\), got Buffer\(Length: 20\)/);
+ v.parse(types.Hash256bitSchema, buffer20byte);
+ }, /ValiError: Invalid length: Expected 32 but received 20/);
});
});
describe('Satoshi', () => {
[
- { value: -1, result: false },
- { value: 0, result: true },
- { value: 1, result: true },
- { value: 20999999 * 1e8, result: true },
- { value: 21000000 * 1e8, result: true },
- { value: 21000001 * 1e8, result: false },
+ { value: BigInt(-1), result: false },
+ { value: BigInt(0), result: true },
+ { value: BigInt(1), result: true },
+ { value: BigInt(20999999 * 1e8), result: true },
+ { value: BigInt(21000000 * 1e8), result: true },
+ { value: BigInt(21000001 * 1e8), result: true },
+ { value: BigInt((1n << 63n) - 1n), result: true },
+ { value: BigInt(1n << 63n), result: false },
].forEach(f => {
it('returns ' + f.result + ' for valid for ' + f.value, () => {
- assert.strictEqual(types.Satoshi(f.value), f.result);
+ assert.strictEqual(v.is(types.SatoshiSchema, f.value), f.result);
});
});
});
-
- describe('UInt31', () => {
- const UINT31_MAX = Math.pow(2, 31) - 1;
- it('return true for valid values', () => {
- assert.strictEqual(types.UInt31(0), true);
- assert.strictEqual(types.UInt31(1000), true);
- assert.strictEqual(types.UInt31(UINT31_MAX), true);
- });
-
- it('return false for negative values', () => {
- assert.strictEqual(types.UInt31(-1), false);
- assert.strictEqual(types.UInt31(-UINT31_MAX), false);
- });
-
- it(`return false for value > ${UINT31_MAX}`, () => {
- assert.strictEqual(types.UInt31(UINT31_MAX + 1), false);
- });
- });
-
- describe('BIP32Path', () => {
- it('return true for valid paths', () => {
- assert.strictEqual(types.BIP32Path("m/0'/0'"), true);
- assert.strictEqual(types.BIP32Path("m/0'/0"), true);
- assert.strictEqual(types.BIP32Path("m/0'/1'/2'/3/4'"), true);
- });
-
- it('return false for invalid paths', () => {
- assert.strictEqual(types.BIP32Path('m'), false);
- assert.strictEqual(types.BIP32Path("n/0'/0'"), false);
- assert.strictEqual(types.BIP32Path("m/0'/x"), false);
- });
-
- it('return "BIP32 derivation path" for JSON.strigify()', () => {
- const toJsonValue = JSON.stringify(types.BIP32Path);
- assert.equal(toJsonValue, '"BIP32 derivation path"');
- });
- });
});
diff --git a/ts_src/address.ts b/ts_src/address.ts
index d8111a7e4..9b551e0d5 100644
--- a/ts_src/address.ts
+++ b/ts_src/address.ts
@@ -1,30 +1,60 @@
-import { Network } from './networks';
-import * as networks from './networks';
-import * as payments from './payments';
-import * as bscript from './script';
-import * as types from './types';
+/**
+ * bitcoin address decode and encode tools, include base58、bech32 and output script
+ *
+ * networks support bitcoin、bitcoin testnet and bitcoin regtest
+ *
+ * addresses support P2PKH、P2SH、P2WPKH、P2WSH、P2TR and so on
+ *
+ * @packageDocumentation
+ */
+import { Network } from './networks.js';
+import * as networks from './networks.js';
+import * as payments from './payments/index.js';
+import * as bscript from './script.js';
+import { Hash160bitSchema, UInt8Schema } from './types.js';
import { bech32, bech32m } from 'bech32';
-import * as bs58check from 'bs58check';
-const { typeforce } = types;
+import bs58check from 'bs58check';
+import * as tools from 'uint8array-tools';
+import * as v from 'valibot';
+/** base58check decode result */
export interface Base58CheckResult {
- hash: Buffer;
+ /** address hash */
+ hash: Uint8Array;
+ /** address version: 0x00 for P2PKH, 0x05 for P2SH */
version: number;
}
+/** bech32 decode result */
export interface Bech32Result {
+ /** address version: 0x00 for P2WPKH、P2WSH, 0x01 for P2TR*/
version: number;
+ /** address prefix: bc for P2WPKH、P2WSH、P2TR */
prefix: string;
- data: Buffer;
+ /** address data:20 bytes for P2WPKH, 32 bytes for P2WSH、P2TR */
+ data: Uint8Array;
}
const FUTURE_SEGWIT_MAX_SIZE: number = 40;
const FUTURE_SEGWIT_MIN_SIZE: number = 2;
const FUTURE_SEGWIT_MAX_VERSION: number = 16;
-const FUTURE_SEGWIT_MIN_VERSION: number = 1;
+const FUTURE_SEGWIT_MIN_VERSION: number = 2;
const FUTURE_SEGWIT_VERSION_DIFF: number = 0x50;
+const FUTURE_SEGWIT_VERSION_WARNING: string =
+ 'WARNING: Sending to a future segwit version address can lead to loss of funds. ' +
+ 'End users MUST be warned carefully in the GUI and asked if they wish to proceed ' +
+ 'with caution. Wallets should verify the segwit version from the output of fromBech32, ' +
+ 'then decide when it is safe to use which version of segwit.';
+const WARNING_STATES: boolean[] = [false, false];
-function _toFutureSegwitAddress(output: Buffer, network: Network): string {
+/**
+ * Converts an output buffer to a future segwit address.
+ * @param output - The output buffer.
+ * @param network - The network object.
+ * @returns The future segwit address.
+ * @throws {TypeError} If the program length or version is invalid for segwit address.
+ */
+function _toFutureSegwitAddress(output: Uint8Array, network: Network): string {
const data = output.slice(2);
if (
@@ -44,9 +74,21 @@ function _toFutureSegwitAddress(output: Buffer, network: Network): string {
if (output[1] !== data.length)
throw new TypeError('Invalid script for segwit address');
+ if (WARNING_STATES[0] === false) {
+ console.warn(FUTURE_SEGWIT_VERSION_WARNING);
+ WARNING_STATES[0] = true;
+ }
+
return toBech32(data, version, network.bech32);
}
+/**
+ * Decodes a base58check encoded Bitcoin address and returns the version and hash.
+ *
+ * @param address - The base58check encoded Bitcoin address to decode.
+ * @returns An object containing the version and hash of the decoded address.
+ * @throws {TypeError} If the address is too short or too long.
+ */
export function fromBase58Check(address: string): Base58CheckResult {
const payload = bs58check.decode(address);
@@ -54,12 +96,18 @@ export function fromBase58Check(address: string): Base58CheckResult {
if (payload.length < 21) throw new TypeError(address + ' is too short');
if (payload.length > 21) throw new TypeError(address + ' is too long');
- const version = payload.readUInt8(0);
+ const version = tools.readUInt8(payload, 0);
const hash = payload.slice(1);
return { version, hash };
}
+/**
+ * Converts a Bech32 or Bech32m encoded address to its corresponding data representation.
+ * @param address - The Bech32 or Bech32m encoded address.
+ * @returns An object containing the version, prefix, and data of the address.
+ * @throws {TypeError} If the address uses the wrong encoding.
+ */
export function fromBech32(address: string): Bech32Result {
let result;
let version;
@@ -81,22 +129,35 @@ export function fromBech32(address: string): Bech32Result {
return {
version,
prefix: result.prefix,
- data: Buffer.from(data),
+ data: Uint8Array.from(data),
};
}
-export function toBase58Check(hash: Buffer, version: number): string {
- typeforce(types.tuple(types.Hash160bit, types.UInt8), arguments);
+/**
+ * Converts a hash to a Base58Check-encoded string.
+ * @param hash - The hash to be encoded.
+ * @param version - The version byte to be prepended to the encoded string.
+ * @returns The Base58Check-encoded string.
+ */
+export function toBase58Check(hash: Uint8Array, version: number): string {
+ v.parse(v.tuple([Hash160bitSchema, UInt8Schema]), [hash, version]);
- const payload = Buffer.allocUnsafe(21);
- payload.writeUInt8(version, 0);
- hash.copy(payload, 1);
+ const payload = new Uint8Array(21);
+ tools.writeUInt8(payload, 0, version);
+ payload.set(hash, 1);
return bs58check.encode(payload);
}
+/**
+ * Converts a buffer to a Bech32 or Bech32m encoded string.
+ * @param data - The buffer to be encoded.
+ * @param version - The version number to be used in the encoding.
+ * @param prefix - The prefix string to be used in the encoding.
+ * @returns The Bech32 or Bech32m encoded string.
+ */
export function toBech32(
- data: Buffer,
+ data: Uint8Array,
version: number,
prefix: string,
): string {
@@ -108,7 +169,17 @@ export function toBech32(
: bech32m.encode(prefix, words);
}
-export function fromOutputScript(output: Buffer, network?: Network): string {
+/**
+ * Converts an output script to a Bitcoin address.
+ * @param output - The output script as a Buffer.
+ * @param network - The Bitcoin network (optional).
+ * @returns The Bitcoin address corresponding to the output script.
+ * @throws If the output script has no matching address.
+ */
+export function fromOutputScript(
+ output: Uint8Array,
+ network?: Network,
+): string {
// TODO: Network
network = network || networks.bitcoin;
@@ -124,6 +195,9 @@ export function fromOutputScript(output: Buffer, network?: Network): string {
try {
return payments.p2wsh({ output, network }).address as string;
} catch (e) {}
+ try {
+ return payments.p2tr({ output, network }).address as string;
+ } catch (e) {}
try {
return _toFutureSegwitAddress(output, network);
} catch (e) {}
@@ -131,7 +205,14 @@ export function fromOutputScript(output: Buffer, network?: Network): string {
throw new Error(bscript.toASM(output) + ' has no matching Address');
}
-export function toOutputScript(address: string, network?: Network): Buffer {
+/**
+ * Converts a Bitcoin address to its corresponding output script.
+ * @param address - The Bitcoin address to convert.
+ * @param network - The Bitcoin network to use. Defaults to the Bitcoin network.
+ * @returns The corresponding output script as a Buffer.
+ * @throws If the address has an invalid prefix or no matching script.
+ */
+export function toOutputScript(address: string, network?: Network): Uint8Array {
network = network || networks.bitcoin;
let decodeBase58: Base58CheckResult | undefined;
@@ -142,9 +223,9 @@ export function toOutputScript(address: string, network?: Network): Buffer {
if (decodeBase58) {
if (decodeBase58.version === network.pubKeyHash)
- return payments.p2pkh({ hash: decodeBase58.hash }).output as Buffer;
+ return payments.p2pkh({ hash: decodeBase58.hash }).output as Uint8Array;
if (decodeBase58.version === network.scriptHash)
- return payments.p2sh({ hash: decodeBase58.hash }).output as Buffer;
+ return payments.p2sh({ hash: decodeBase58.hash }).output as Uint8Array;
} else {
try {
decodeBech32 = fromBech32(address);
@@ -155,19 +236,31 @@ export function toOutputScript(address: string, network?: Network): Buffer {
throw new Error(address + ' has an invalid prefix');
if (decodeBech32.version === 0) {
if (decodeBech32.data.length === 20)
- return payments.p2wpkh({ hash: decodeBech32.data }).output as Buffer;
+ return payments.p2wpkh({ hash: decodeBech32.data })
+ .output as Uint8Array;
+ if (decodeBech32.data.length === 32)
+ return payments.p2wsh({ hash: decodeBech32.data })
+ .output as Uint8Array;
+ } else if (decodeBech32.version === 1) {
if (decodeBech32.data.length === 32)
- return payments.p2wsh({ hash: decodeBech32.data }).output as Buffer;
+ return payments.p2tr({ pubkey: decodeBech32.data })
+ .output as Uint8Array;
} else if (
decodeBech32.version >= FUTURE_SEGWIT_MIN_VERSION &&
decodeBech32.version <= FUTURE_SEGWIT_MAX_VERSION &&
decodeBech32.data.length >= FUTURE_SEGWIT_MIN_SIZE &&
decodeBech32.data.length <= FUTURE_SEGWIT_MAX_SIZE
- )
+ ) {
+ if (WARNING_STATES[1] === false) {
+ console.warn(FUTURE_SEGWIT_VERSION_WARNING);
+ WARNING_STATES[1] = true;
+ }
+
return bscript.compile([
decodeBech32.version + FUTURE_SEGWIT_VERSION_DIFF,
decodeBech32.data,
]);
+ }
}
}
diff --git a/ts_src/bip66.ts b/ts_src/bip66.ts
index ab76a4fd3..03d57197e 100644
--- a/ts_src/bip66.ts
+++ b/ts_src/bip66.ts
@@ -2,7 +2,13 @@
// Format: 0x30 [total-length] 0x02 [R-length] [R] 0x02 [S-length] [S]
// NOTE: SIGHASH byte ignored AND restricted, truncate before use
-export function check(buffer: Buffer): boolean {
+/**
+ * Checks if the given buffer is a valid BIP66-encoded signature.
+ *
+ * @param buffer - The buffer to check.
+ * @returns A boolean indicating whether the buffer is a valid BIP66-encoded signature.
+ */
+export function check(buffer: Uint8Array): boolean {
if (buffer.length < 8) return false;
if (buffer.length > 72) return false;
if (buffer[0] !== 0x30) return false;
@@ -27,7 +33,15 @@ export function check(buffer: Buffer): boolean {
return true;
}
-export function decode(buffer: Buffer): { r: Buffer; s: Buffer } {
+/**
+ * Decodes a DER-encoded signature buffer and returns the R and S values.
+ * @param buffer - The DER-encoded signature buffer.
+ * @returns An object containing the R and S values.
+ * @throws {Error} If the DER sequence length is too short, too long, or invalid.
+ * @throws {Error} If the R or S length is zero or invalid.
+ * @throws {Error} If the R or S value is negative or excessively padded.
+ */
+export function decode(buffer: Uint8Array): { r: Uint8Array; s: Uint8Array } {
if (buffer.length < 8) throw new Error('DER sequence length is too short');
if (buffer.length > 72) throw new Error('DER sequence length is too long');
if (buffer[0] !== 0x30) throw new Error('Expected DER sequence');
@@ -81,7 +95,7 @@ export function decode(buffer: Buffer): { r: Buffer; s: Buffer } {
* 62300 => 0x00f35c
* -62300 => 0xff0ca4
*/
-export function encode(r: Buffer, s: Buffer): Buffer {
+export function encode(r: Uint8Array, s: Uint8Array): Uint8Array {
const lenR = r.length;
const lenS = s.length;
if (lenR === 0) throw new Error('R length is zero');
@@ -95,17 +109,17 @@ export function encode(r: Buffer, s: Buffer): Buffer {
if (lenS > 1 && s[0] === 0x00 && !(s[1] & 0x80))
throw new Error('S value excessively padded');
- const signature = Buffer.allocUnsafe(6 + lenR + lenS);
+ const signature = new Uint8Array(6 + lenR + lenS);
// 0x30 [total-length] 0x02 [R-length] [R] 0x02 [S-length] [S]
signature[0] = 0x30;
signature[1] = signature.length - 2;
signature[2] = 0x02;
signature[3] = r.length;
- r.copy(signature, 4);
+ signature.set(r, 4);
signature[4 + lenR] = 0x02;
signature[5 + lenR] = s.length;
- s.copy(signature, 6 + lenR);
+ signature.set(s, 6 + lenR);
return signature;
}
diff --git a/ts_src/block.ts b/ts_src/block.ts
index c73477e02..5d065a19f 100644
--- a/ts_src/block.ts
+++ b/ts_src/block.ts
@@ -3,12 +3,12 @@ import {
BufferWriter,
reverseBuffer,
varuint,
-} from './bufferutils';
-import * as bcrypto from './crypto';
-import { fastMerkleRoot } from './merkle';
-import { Transaction } from './transaction';
-import * as types from './types';
-const { typeforce } = types;
+} from './bufferutils.js';
+import * as bcrypto from './crypto.js';
+import { fastMerkleRoot } from './merkle.js';
+import { Transaction } from './transaction.js';
+import * as v from 'valibot';
+import * as tools from 'uint8array-tools';
const errorMerkleNoTxes = new TypeError(
'Cannot compute merkle root for zero transactions',
@@ -18,7 +18,7 @@ const errorWitnessNotSegwit = new TypeError(
);
export class Block {
- static fromBuffer(buffer: Buffer): Block {
+ static fromBuffer(buffer: Uint8Array): Block {
if (buffer.length < 80) throw new Error('Buffer too small (< 80 bytes)');
const bufferReader = new BufferReader(buffer);
@@ -58,22 +58,24 @@ export class Block {
}
static fromHex(hex: string): Block {
- return Block.fromBuffer(Buffer.from(hex, 'hex'));
+ return Block.fromBuffer(tools.fromHex(hex));
}
- static calculateTarget(bits: number): Buffer {
+ static calculateTarget(bits: number): Uint8Array {
const exponent = ((bits & 0xff000000) >> 24) - 3;
const mantissa = bits & 0x007fffff;
- const target = Buffer.alloc(32, 0);
- target.writeUIntBE(mantissa, 29 - exponent, 3);
+ const target = new Uint8Array(32);
+ target[29 - exponent] = (mantissa >> 16) & 0xff;
+ target[30 - exponent] = (mantissa >> 8) & 0xff;
+ target[31 - exponent] = mantissa & 0xff;
return target;
}
static calculateMerkleRoot(
transactions: Transaction[],
forWitness?: boolean,
- ): Buffer {
- typeforce([{ getHash: types.Function }], transactions);
+ ): Uint8Array {
+ v.parse(v.array(v.object({ getHash: v.function() })), transactions);
if (transactions.length === 0) throw errorMerkleNoTxes;
if (forWitness && !txesHaveWitnessCommit(transactions))
throw errorWitnessNotSegwit;
@@ -86,41 +88,45 @@ export class Block {
return forWitness
? bcrypto.hash256(
- Buffer.concat([rootHash, transactions[0].ins[0].witness[0]]),
+ tools.concat([rootHash, transactions[0].ins[0].witness[0]]),
)
: rootHash;
}
version: number = 1;
- prevHash?: Buffer = undefined;
- merkleRoot?: Buffer = undefined;
+ prevHash?: Uint8Array = undefined;
+ merkleRoot?: Uint8Array = undefined;
timestamp: number = 0;
- witnessCommit?: Buffer = undefined;
+ witnessCommit?: Uint8Array = undefined;
bits: number = 0;
nonce: number = 0;
transactions?: Transaction[] = undefined;
- getWitnessCommit(): Buffer | null {
+ getWitnessCommit(): Uint8Array | null {
if (!txesHaveWitnessCommit(this.transactions!)) return null;
// The merkle root for the witness data is in an OP_RETURN output.
// There is no rule for the index of the output, so use filter to find it.
// The root is prepended with 0xaa21a9ed so check for 0x6a24aa21a9ed
// If multiple commits are found, the output with highest index is assumed.
- const witnessCommits = this.transactions![0].outs.filter(out =>
- out.script.slice(0, 6).equals(Buffer.from('6a24aa21a9ed', 'hex')),
+ const witnessCommits = this.transactions![0].outs.filter(
+ out =>
+ tools.compare(
+ out.script.slice(0, 6),
+ Uint8Array.from([0x6a, 0x24, 0xaa, 0x21, 0xa9, 0xed]),
+ ) === 0,
).map(out => out.script.slice(6, 38));
if (witnessCommits.length === 0) return null;
// Use the commit with the highest output (should only be one though)
const result = witnessCommits[witnessCommits.length - 1];
- if (!(result instanceof Buffer && result.length === 32)) return null;
+ if (!(result instanceof Uint8Array && result.length === 32)) return null;
return result;
}
hasWitnessCommit(): boolean {
if (
- this.witnessCommit instanceof Buffer &&
+ this.witnessCommit instanceof Uint8Array &&
this.witnessCommit.length === 32
)
return true;
@@ -148,12 +154,12 @@ export class Block {
);
}
- getHash(): Buffer {
+ getHash(): Uint8Array {
return bcrypto.hash256(this.toBuffer(true));
}
getId(): string {
- return reverseBuffer(this.getHash()).toString('hex');
+ return tools.toHex(reverseBuffer(this.getHash()));
}
getUTCDate(): Date {
@@ -164,8 +170,8 @@ export class Block {
}
// TODO: buffer, offset compatibility
- toBuffer(headersOnly?: boolean): Buffer {
- const buffer: Buffer = Buffer.allocUnsafe(this.byteLength(headersOnly));
+ toBuffer(headersOnly?: boolean): Uint8Array {
+ const buffer = new Uint8Array(this.byteLength(headersOnly));
const bufferWriter = new BufferWriter(buffer);
@@ -178,8 +184,12 @@ export class Block {
if (headersOnly || !this.transactions) return buffer;
- varuint.encode(this.transactions.length, buffer, bufferWriter.offset);
- bufferWriter.offset += varuint.encode.bytes;
+ const { bytes } = varuint.encode(
+ this.transactions.length,
+ buffer,
+ bufferWriter.offset,
+ );
+ bufferWriter.offset += bytes;
this.transactions.forEach(tx => {
const txSize = tx.byteLength(); // TODO: extract from toBuffer?
@@ -191,7 +201,7 @@ export class Block {
}
toHex(headersOnly?: boolean): string {
- return this.toBuffer(headersOnly).toString('hex');
+ return tools.toHex(this.toBuffer(headersOnly));
}
checkTxRoots(): boolean {
@@ -206,17 +216,17 @@ export class Block {
}
checkProofOfWork(): boolean {
- const hash: Buffer = reverseBuffer(this.getHash());
+ const hash = reverseBuffer(this.getHash());
const target = Block.calculateTarget(this.bits);
- return hash.compare(target) <= 0;
+ return tools.compare(hash, target) <= 0;
}
private __checkMerkleRoot(): boolean {
if (!this.transactions) throw errorMerkleNoTxes;
const actualMerkleRoot = Block.calculateMerkleRoot(this.transactions);
- return this.merkleRoot!.compare(actualMerkleRoot) === 0;
+ return tools.compare(this.merkleRoot!, actualMerkleRoot) === 0;
}
private __checkWitnessCommit(): boolean {
@@ -227,7 +237,7 @@ export class Block {
this.transactions,
true,
);
- return this.witnessCommit!.compare(actualWitnessCommit) === 0;
+ return tools.compare(this.witnessCommit!, actualWitnessCommit) === 0;
}
}
diff --git a/ts_src/bufferutils.ts b/ts_src/bufferutils.ts
index 901d72a48..d8b7dfb0e 100644
--- a/ts_src/bufferutils.ts
+++ b/ts_src/bufferutils.ts
@@ -1,41 +1,29 @@
-import * as types from './types';
-const { typeforce } = types;
+import * as types from './types.js';
import * as varuint from 'varuint-bitcoin';
+import * as v from 'valibot';
export { varuint };
+import * as tools from 'uint8array-tools';
+
+const MAX_JS_NUMBER = 0x001fffffffffffff;
// https://github.com/feross/buffer/blob/master/index.js#L1127
-function verifuint(value: number, max: number): void {
- if (typeof value !== 'number')
+function verifuint(value: number | bigint, max: number): void {
+ if (typeof value !== 'number' && typeof value !== 'bigint')
throw new Error('cannot write a non-number as a number');
- if (value < 0)
+ if (value < 0 && value < BigInt(0))
throw new Error('specified a negative value for writing an unsigned value');
- if (value > max) throw new Error('RangeError: value out of range');
- if (Math.floor(value) !== value)
+ if (value > max && value > BigInt(max))
+ throw new Error('RangeError: value out of range');
+ if (Math.floor(Number(value)) !== Number(value))
throw new Error('value has a fractional component');
}
-export function readUInt64LE(buffer: Buffer, offset: number): number {
- const a = buffer.readUInt32LE(offset);
- let b = buffer.readUInt32LE(offset + 4);
- b *= 0x100000000;
-
- verifuint(b + a, 0x001fffffffffffff);
- return b + a;
-}
-
-export function writeUInt64LE(
- buffer: Buffer,
- value: number,
- offset: number,
-): number {
- verifuint(value, 0x001fffffffffffff);
-
- buffer.writeInt32LE(value & -1, offset);
- buffer.writeUInt32LE(Math.floor(value / 0x100000000), offset + 4);
- return offset + 8;
-}
-
-export function reverseBuffer(buffer: Buffer): Buffer {
+/**
+ * Reverses the order of bytes in a buffer.
+ * @param buffer - The buffer to reverse.
+ * @returns A new buffer with the bytes reversed.
+ */
+export function reverseBuffer(buffer: Uint8Array): Uint8Array {
if (buffer.length < 1) return buffer;
let j = buffer.length - 1;
let tmp = 0;
@@ -48,9 +36,9 @@ export function reverseBuffer(buffer: Buffer): Buffer {
return buffer;
}
-export function cloneBuffer(buffer: Buffer): Buffer {
- const clone = Buffer.allocUnsafe(buffer.length);
- buffer.copy(clone);
+export function cloneBuffer(buffer: Uint8Array): Uint8Array {
+ const clone = new Uint8Array(buffer.length);
+ clone.set(buffer);
return clone;
}
@@ -59,52 +47,63 @@ export function cloneBuffer(buffer: Buffer): Buffer {
*/
export class BufferWriter {
static withCapacity(size: number): BufferWriter {
- return new BufferWriter(Buffer.alloc(size));
+ return new BufferWriter(new Uint8Array(size));
}
- constructor(public buffer: Buffer, public offset: number = 0) {
- typeforce(types.tuple(types.Buffer, types.UInt32), [buffer, offset]);
+ constructor(
+ public buffer: Uint8Array,
+ public offset: number = 0,
+ ) {
+ v.parse(v.tuple([types.BufferSchema, types.UInt32Schema]), [
+ buffer,
+ offset,
+ ]);
}
writeUInt8(i: number): void {
- this.offset = this.buffer.writeUInt8(i, this.offset);
+ this.offset = tools.writeUInt8(this.buffer, this.offset, i);
}
writeInt32(i: number): void {
- this.offset = this.buffer.writeInt32LE(i, this.offset);
+ this.offset = tools.writeInt32(this.buffer, this.offset, i, 'LE');
+ }
+
+ writeInt64(i: number | bigint): void {
+ this.offset = tools.writeInt64(this.buffer, this.offset, BigInt(i), 'LE');
}
writeUInt32(i: number): void {
- this.offset = this.buffer.writeUInt32LE(i, this.offset);
+ this.offset = tools.writeUInt32(this.buffer, this.offset, i, 'LE');
}
- writeUInt64(i: number): void {
- this.offset = writeUInt64LE(this.buffer, i, this.offset);
+ writeUInt64(i: number | bigint): void {
+ this.offset = tools.writeUInt64(this.buffer, this.offset, BigInt(i), 'LE');
}
writeVarInt(i: number): void {
- varuint.encode(i, this.buffer, this.offset);
- this.offset += varuint.encode.bytes;
+ const { bytes } = varuint.encode(i, this.buffer, this.offset);
+ this.offset += bytes;
}
- writeSlice(slice: Buffer): void {
+ writeSlice(slice: Uint8Array): void {
if (this.buffer.length < this.offset + slice.length) {
throw new Error('Cannot write slice out of bounds');
}
- this.offset += slice.copy(this.buffer, this.offset);
+ this.buffer.set(slice, this.offset);
+ this.offset += slice.length;
}
- writeVarSlice(slice: Buffer): void {
+ writeVarSlice(slice: Uint8Array): void {
this.writeVarInt(slice.length);
this.writeSlice(slice);
}
- writeVector(vector: Buffer[]): void {
+ writeVector(vector: Uint8Array[]): void {
this.writeVarInt(vector.length);
- vector.forEach((buf: Buffer) => this.writeVarSlice(buf));
+ vector.forEach((buf: Uint8Array) => this.writeVarSlice(buf));
}
- end(): Buffer {
+ end(): Uint8Array {
if (this.buffer.length === this.offset) {
return this.buffer;
}
@@ -116,56 +115,64 @@ export class BufferWriter {
* Helper class for reading of bitcoin data types from a buffer.
*/
export class BufferReader {
- constructor(public buffer: Buffer, public offset: number = 0) {
- typeforce(types.tuple(types.Buffer, types.UInt32), [buffer, offset]);
+ constructor(
+ public buffer: Uint8Array,
+ public offset: number = 0,
+ ) {
+ v.parse(v.tuple([types.BufferSchema, types.UInt32Schema]), [
+ buffer,
+ offset,
+ ]);
}
readUInt8(): number {
- const result = this.buffer.readUInt8(this.offset);
+ const result = tools.readUInt8(this.buffer, this.offset);
this.offset++;
return result;
}
readInt32(): number {
- const result = this.buffer.readInt32LE(this.offset);
+ const result = tools.readInt32(this.buffer, this.offset, 'LE');
this.offset += 4;
return result;
}
readUInt32(): number {
- const result = this.buffer.readUInt32LE(this.offset);
+ const result = tools.readUInt32(this.buffer, this.offset, 'LE');
this.offset += 4;
return result;
}
- readUInt64(): number {
- const result = readUInt64LE(this.buffer, this.offset);
+ readInt64(): bigint {
+ const result = tools.readInt64(this.buffer, this.offset, 'LE');
this.offset += 8;
return result;
}
- readVarInt(): number {
- const vi = varuint.decode(this.buffer, this.offset);
- this.offset += varuint.decode.bytes;
- return vi;
+ readVarInt(): bigint {
+ const { bigintValue, bytes } = varuint.decode(this.buffer, this.offset);
+ this.offset += bytes;
+ return bigintValue;
}
- readSlice(n: number): Buffer {
- if (this.buffer.length < this.offset + n) {
+ readSlice(n: number | bigint): Uint8Array {
+ verifuint(n, MAX_JS_NUMBER);
+ const num = Number(n);
+ if (this.buffer.length < this.offset + num) {
throw new Error('Cannot read slice out of bounds');
}
- const result = this.buffer.slice(this.offset, this.offset + n);
- this.offset += n;
+ const result = this.buffer.slice(this.offset, this.offset + num);
+ this.offset += num;
return result;
}
- readVarSlice(): Buffer {
+ readVarSlice(): Uint8Array {
return this.readSlice(this.readVarInt());
}
- readVector(): Buffer[] {
+ readVector(): Uint8Array[] {
const count = this.readVarInt();
- const vector: Buffer[] = [];
+ const vector: Uint8Array[] = [];
for (let i = 0; i < count; i++) vector.push(this.readVarSlice());
return vector;
}
diff --git a/ts_src/crypto.ts b/ts_src/crypto.ts
index b7c355a73..7163c55ba 100644
--- a/ts_src/crypto.ts
+++ b/ts_src/crypto.ts
@@ -1,38 +1,37 @@
-import * as createHash from 'create-hash';
+/**
+ * A module for hashing functions.
+ * include ripemd160、sha1、sha256、hash160、hash256、taggedHash
+ *
+ * @packageDocumentation
+ */
+import { ripemd160 } from '@noble/hashes/ripemd160';
+import { sha256 } from '@noble/hashes/sha256';
+import * as tools from 'uint8array-tools';
-export function ripemd160(buffer: Buffer): Buffer {
- try {
- return createHash('rmd160')
- .update(buffer)
- .digest();
- } catch (err) {
- return createHash('ripemd160')
- .update(buffer)
- .digest();
- }
-}
-
-export function sha1(buffer: Buffer): Buffer {
- return createHash('sha1')
- .update(buffer)
- .digest();
-}
+export { ripemd160, sha256 };
+export { sha1 } from '@noble/hashes/sha1';
-export function sha256(buffer: Buffer): Buffer {
- return createHash('sha256')
- .update(buffer)
- .digest();
-}
-
-export function hash160(buffer: Buffer): Buffer {
+/**
+ * Computes the HASH160 (RIPEMD-160 after SHA-256) of the given buffer.
+ *
+ * @param buffer - The input data to be hashed.
+ * @returns The HASH160 of the input buffer.
+ */
+export function hash160(buffer: Uint8Array): Uint8Array {
return ripemd160(sha256(buffer));
}
-export function hash256(buffer: Buffer): Buffer {
+/**
+ * Computes the double SHA-256 hash of the given buffer.
+ *
+ * @param buffer - The input data to be hashed.
+ * @returns The double SHA-256 hash of the input buffer.
+ */
+export function hash256(buffer: Uint8Array): Uint8Array {
return sha256(sha256(buffer));
}
-const TAGS = [
+export const TAGS = [
'BIP0340/challenge',
'BIP0340/aux',
'BIP0340/nonce',
@@ -43,15 +42,95 @@ const TAGS = [
'KeyAgg list',
'KeyAgg coefficient',
] as const;
-export type TaggedHashPrefix = typeof TAGS[number];
-/** An object mapping tags to their tagged hash prefix of [SHA256(tag) | SHA256(tag)] */
-const TAGGED_HASH_PREFIXES = Object.fromEntries(
- TAGS.map(tag => {
- const tagHash = sha256(Buffer.from(tag));
- return [tag, Buffer.concat([tagHash, tagHash])];
- }),
-) as { [k in TaggedHashPrefix]: Buffer };
+export type TaggedHashPrefix = (typeof TAGS)[number];
+type TaggedHashPrefixes = {
+ [key in TaggedHashPrefix]: Uint8Array;
+};
+
+/**
+ * A collection of tagged hash prefixes used in various BIP (Bitcoin Improvement Proposals)
+ * and Taproot-related operations. Each prefix is represented as a `Uint8Array`.
+ *
+ * @constant
+ * @type {TaggedHashPrefixes}
+ *
+ * @property {'BIP0340/challenge'} - Prefix for BIP0340 challenge.
+ * @property {'BIP0340/aux'} - Prefix for BIP0340 auxiliary data.
+ * @property {'BIP0340/nonce'} - Prefix for BIP0340 nonce.
+ * @property {TapLeaf} - Prefix for Taproot leaf.
+ * @property {TapBranch} - Prefix for Taproot branch.
+ * @property {TapSighash} - Prefix for Taproot sighash.
+ * @property {TapTweak} - Prefix for Taproot tweak.
+ * @property {'KeyAgg list'} - Prefix for key aggregation list.
+ * @property {'KeyAgg coefficient'} - Prefix for key aggregation coefficient.
+ */
+export const TAGGED_HASH_PREFIXES: TaggedHashPrefixes = {
+ 'BIP0340/challenge': Uint8Array.from([
+ 123, 181, 45, 122, 159, 239, 88, 50, 62, 177, 191, 122, 64, 125, 179, 130,
+ 210, 243, 242, 216, 27, 177, 34, 79, 73, 254, 81, 143, 109, 72, 211, 124,
+ 123, 181, 45, 122, 159, 239, 88, 50, 62, 177, 191, 122, 64, 125, 179, 130,
+ 210, 243, 242, 216, 27, 177, 34, 79, 73, 254, 81, 143, 109, 72, 211, 124,
+ ]),
+ 'BIP0340/aux': Uint8Array.from([
+ 241, 239, 78, 94, 192, 99, 202, 218, 109, 148, 202, 250, 157, 152, 126, 160,
+ 105, 38, 88, 57, 236, 193, 31, 151, 45, 119, 165, 46, 216, 193, 204, 144,
+ 241, 239, 78, 94, 192, 99, 202, 218, 109, 148, 202, 250, 157, 152, 126, 160,
+ 105, 38, 88, 57, 236, 193, 31, 151, 45, 119, 165, 46, 216, 193, 204, 144,
+ ]),
+ 'BIP0340/nonce': Uint8Array.from([
+ 7, 73, 119, 52, 167, 155, 203, 53, 91, 155, 140, 125, 3, 79, 18, 28, 244,
+ 52, 215, 62, 247, 45, 218, 25, 135, 0, 97, 251, 82, 191, 235, 47, 7, 73,
+ 119, 52, 167, 155, 203, 53, 91, 155, 140, 125, 3, 79, 18, 28, 244, 52, 215,
+ 62, 247, 45, 218, 25, 135, 0, 97, 251, 82, 191, 235, 47,
+ ]),
+ TapLeaf: Uint8Array.from([
+ 174, 234, 143, 220, 66, 8, 152, 49, 5, 115, 75, 88, 8, 29, 30, 38, 56, 211,
+ 95, 28, 181, 64, 8, 212, 211, 87, 202, 3, 190, 120, 233, 238, 174, 234, 143,
+ 220, 66, 8, 152, 49, 5, 115, 75, 88, 8, 29, 30, 38, 56, 211, 95, 28, 181,
+ 64, 8, 212, 211, 87, 202, 3, 190, 120, 233, 238,
+ ]),
+ TapBranch: Uint8Array.from([
+ 25, 65, 161, 242, 229, 110, 185, 95, 162, 169, 241, 148, 190, 92, 1, 247,
+ 33, 111, 51, 237, 130, 176, 145, 70, 52, 144, 208, 91, 245, 22, 160, 21, 25,
+ 65, 161, 242, 229, 110, 185, 95, 162, 169, 241, 148, 190, 92, 1, 247, 33,
+ 111, 51, 237, 130, 176, 145, 70, 52, 144, 208, 91, 245, 22, 160, 21,
+ ]),
+ TapSighash: Uint8Array.from([
+ 244, 10, 72, 223, 75, 42, 112, 200, 180, 146, 75, 242, 101, 70, 97, 237, 61,
+ 149, 253, 102, 163, 19, 235, 135, 35, 117, 151, 198, 40, 228, 160, 49, 244,
+ 10, 72, 223, 75, 42, 112, 200, 180, 146, 75, 242, 101, 70, 97, 237, 61, 149,
+ 253, 102, 163, 19, 235, 135, 35, 117, 151, 198, 40, 228, 160, 49,
+ ]),
+ TapTweak: Uint8Array.from([
+ 232, 15, 225, 99, 156, 156, 160, 80, 227, 175, 27, 57, 193, 67, 198, 62, 66,
+ 156, 188, 235, 21, 217, 64, 251, 181, 197, 161, 244, 175, 87, 197, 233, 232,
+ 15, 225, 99, 156, 156, 160, 80, 227, 175, 27, 57, 193, 67, 198, 62, 66, 156,
+ 188, 235, 21, 217, 64, 251, 181, 197, 161, 244, 175, 87, 197, 233,
+ ]),
+ 'KeyAgg list': Uint8Array.from([
+ 72, 28, 151, 28, 60, 11, 70, 215, 240, 178, 117, 174, 89, 141, 78, 44, 126,
+ 215, 49, 156, 89, 74, 92, 110, 199, 158, 160, 212, 153, 2, 148, 240, 72, 28,
+ 151, 28, 60, 11, 70, 215, 240, 178, 117, 174, 89, 141, 78, 44, 126, 215, 49,
+ 156, 89, 74, 92, 110, 199, 158, 160, 212, 153, 2, 148, 240,
+ ]),
+ 'KeyAgg coefficient': Uint8Array.from([
+ 191, 201, 4, 3, 77, 28, 136, 232, 200, 14, 34, 229, 61, 36, 86, 109, 100,
+ 130, 78, 214, 66, 114, 129, 192, 145, 0, 249, 77, 205, 82, 201, 129, 191,
+ 201, 4, 3, 77, 28, 136, 232, 200, 14, 34, 229, 61, 36, 86, 109, 100, 130,
+ 78, 214, 66, 114, 129, 192, 145, 0, 249, 77, 205, 82, 201, 129,
+ ]),
+};
-export function taggedHash(prefix: TaggedHashPrefix, data: Buffer): Buffer {
- return sha256(Buffer.concat([TAGGED_HASH_PREFIXES[prefix], data]));
+/**
+ * Computes a tagged hash using the specified prefix and data.
+ *
+ * @param prefix - The prefix to use for the tagged hash. This should be one of the values from the `TaggedHashPrefix` enum.
+ * @param data - The data to hash, provided as a `Uint8Array`.
+ * @returns The resulting tagged hash as a `Uint8Array`.
+ */
+export function taggedHash(
+ prefix: TaggedHashPrefix,
+ data: Uint8Array,
+): Uint8Array {
+ return sha256(tools.concat([TAGGED_HASH_PREFIXES[prefix], data]));
}
diff --git a/ts_src/ecc_lib.ts b/ts_src/ecc_lib.ts
new file mode 100644
index 000000000..ab15e5482
--- /dev/null
+++ b/ts_src/ecc_lib.ts
@@ -0,0 +1,120 @@
+import { TinySecp256k1Interface } from './types.js';
+import * as tools from 'uint8array-tools';
+
+const _ECCLIB_CACHE: { eccLib?: TinySecp256k1Interface } = {};
+
+/**
+ * Initializes the ECC library with the provided instance.
+ * If `eccLib` is `undefined`, the library will be cleared.
+ * If `eccLib` is a new instance, it will be verified before setting it as the active library.
+ *
+ * @param eccLib The instance of the ECC library to initialize.
+ * @param opts Extra initialization options. Use {DANGER_DO_NOT_VERIFY_ECCLIB:true} if ecc verification should not be executed. Not recommended!
+ */
+export function initEccLib(
+ eccLib: TinySecp256k1Interface | undefined,
+ opts?: { DANGER_DO_NOT_VERIFY_ECCLIB: boolean },
+): void {
+ if (!eccLib) {
+ // allow clearing the library
+ _ECCLIB_CACHE.eccLib = eccLib;
+ } else if (eccLib !== _ECCLIB_CACHE.eccLib) {
+ if (!opts?.DANGER_DO_NOT_VERIFY_ECCLIB)
+ // new instance, verify it
+ verifyEcc(eccLib!);
+ _ECCLIB_CACHE.eccLib = eccLib;
+ }
+}
+
+/**
+ * Retrieves the ECC Library instance.
+ * Throws an error if the ECC Library is not provided.
+ * You must call initEccLib() with a valid TinySecp256k1Interface instance before calling this function.
+ * @returns The ECC Library instance.
+ * @throws Error if the ECC Library is not provided.
+ */
+export function getEccLib(): TinySecp256k1Interface {
+ if (!_ECCLIB_CACHE.eccLib)
+ throw new Error(
+ 'No ECC Library provided. You must call initEccLib() with a valid TinySecp256k1Interface instance',
+ );
+ return _ECCLIB_CACHE.eccLib;
+}
+
+const h = (hex: string): Uint8Array => tools.fromHex(hex);
+
+/**
+ * Verifies the ECC functionality.
+ *
+ * @param ecc - The TinySecp256k1Interface object.
+ */
+function verifyEcc(ecc: TinySecp256k1Interface): void {
+ assert(typeof ecc.isXOnlyPoint === 'function');
+ assert(
+ ecc.isXOnlyPoint(
+ h('79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798'),
+ ),
+ );
+ assert(
+ ecc.isXOnlyPoint(
+ h('fffffffffffffffffffffffffffffffffffffffffffffffffffffffeeffffc2e'),
+ ),
+ );
+ assert(
+ ecc.isXOnlyPoint(
+ h('f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9'),
+ ),
+ );
+ assert(
+ ecc.isXOnlyPoint(
+ h('0000000000000000000000000000000000000000000000000000000000000001'),
+ ),
+ );
+ assert(
+ !ecc.isXOnlyPoint(
+ h('0000000000000000000000000000000000000000000000000000000000000000'),
+ ),
+ );
+ assert(
+ !ecc.isXOnlyPoint(
+ h('fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f'),
+ ),
+ );
+
+ assert(typeof ecc.xOnlyPointAddTweak === 'function');
+ tweakAddVectors.forEach(t => {
+ const r = ecc.xOnlyPointAddTweak(h(t.pubkey), h(t.tweak));
+ if (t.result === null) {
+ assert(r === null);
+ } else {
+ assert(r !== null);
+ assert(r!.parity === t.parity);
+ assert(tools.compare(r!.xOnlyPubkey, h(t.result)) === 0);
+ }
+ });
+}
+
+function assert(bool: boolean): void {
+ if (!bool) throw new Error('ecc library invalid');
+}
+
+const tweakAddVectors = [
+ {
+ pubkey: '79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798',
+ tweak: 'fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140',
+ parity: -1,
+ result: null,
+ },
+ {
+ pubkey: '1617d38ed8d8657da4d4761e8057bc396ea9e4b9d29776d4be096016dbd2509b',
+ tweak: 'a8397a935f0dfceba6ba9618f6451ef4d80637abf4e6af2669fbc9de6a8fd2ac',
+ parity: 1,
+ result: 'e478f99dab91052ab39a33ea35fd5e6e4933f4d28023cd597c9a1f6760346adf',
+ },
+ {
+ pubkey: '2c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e668680991',
+ tweak: '823c3cd2142744b075a87eade7e1b8678ba308d566226a0056ca2b7a76f86b47',
+ parity: 0,
+ result: '9534f8dc8c6deda2dc007655981c78b49c5d96c778fbf363462a11ec9dfd948c',
+ },
+];
diff --git a/ts_src/index.ts b/ts_src/index.ts
index d8b8619d1..2a4cb95da 100644
--- a/ts_src/index.ts
+++ b/ts_src/index.ts
@@ -1,13 +1,14 @@
-import * as address from './address';
-import * as crypto from './crypto';
-import * as networks from './networks';
-import * as payments from './payments';
-import * as script from './script';
+import * as address from './address.js';
+import * as crypto from './crypto.js';
+import * as networks from './networks.js';
+import * as payments from './payments/index.js';
+import * as script from './script.js';
export { address, crypto, networks, payments, script };
-export { Block } from './block';
-export { TaggedHashPrefix } from './crypto';
+export { Block } from './block.js';
+/** @hidden */
+export { TaggedHashPrefix } from './crypto.js';
export {
Psbt,
PsbtTxInput,
@@ -16,16 +17,20 @@ export {
SignerAsync,
HDSigner,
HDSignerAsync,
-} from './psbt';
-export { OPS as opcodes } from './ops';
-export { Transaction } from './transaction';
-
-export { Network } from './networks';
+ toXOnly,
+} from './psbt.js';
+/** @hidden */
+export { OPS as opcodes } from './ops.js';
+export { Transaction } from './transaction.js';
+/** @hidden */
+export { Network } from './networks.js';
+/** @hidden */
export {
Payment,
PaymentCreator,
PaymentOpts,
Stack,
StackElement,
-} from './payments';
-export { Input as TxInput, Output as TxOutput } from './transaction';
+} from './payments/index.js';
+export { Input as TxInput, Output as TxOutput } from './transaction.js';
+export { initEccLib } from './ecc_lib.js';
diff --git a/ts_src/merkle.ts b/ts_src/merkle.ts
index 8ff8c3f8c..316155ae0 100644
--- a/ts_src/merkle.ts
+++ b/ts_src/merkle.ts
@@ -1,7 +1,17 @@
+import * as tools from 'uint8array-tools';
+
+/**
+ * Calculates the Merkle root of an array of buffers using a specified digest function.
+ *
+ * @param values - The array of buffers.
+ * @param digestFn - The digest function used to calculate the hash of the concatenated buffers.
+ * @returns The Merkle root as a buffer.
+ * @throws {TypeError} If the values parameter is not an array or the digestFn parameter is not a function.
+ */
export function fastMerkleRoot(
- values: Buffer[],
- digestFn: (b: Buffer) => Buffer,
-): Buffer {
+ values: Uint8Array[],
+ digestFn: (b: Uint8Array) => Uint8Array,
+): Uint8Array {
if (!Array.isArray(values)) throw TypeError('Expected values Array');
if (typeof digestFn !== 'function')
throw TypeError('Expected digest Function');
@@ -15,7 +25,7 @@ export function fastMerkleRoot(
for (let i = 0; i < length; i += 2, ++j) {
const left = results[i];
const right = i + 1 === length ? left : results[i + 1];
- const data = Buffer.concat([left, right]);
+ const data = tools.concat([left, right]);
results[j] = digestFn(data);
}
diff --git a/ts_src/networks.ts b/ts_src/networks.ts
index e66b08c30..0d62a9e5f 100644
--- a/ts_src/networks.ts
+++ b/ts_src/networks.ts
@@ -1,5 +1,13 @@
-// https://en.bitcoin.it/wiki/List_of_address_prefixes
-// Dogecoin BIP32 is a proposed standard: https://bitcointalk.org/index.php?topic=409731
+/**
+ * This module defines the network configurations for Bitcoin and its variants, including message prefixes,
+ * Bech32 address format, BIP32 key derivation prefixes, and other address-related configurations.
+ * It supports Bitcoin, Bitcoin testnet, and Bitcoin regtest networks.
+ *
+ * Additional information on address prefixes can be found here:
+ * - https://en.bitcoin.it/wiki/List_of_address_prefixes
+ *
+ * @packageDocumentation
+ */
export interface Network {
messagePrefix: string;
bech32: string;
@@ -14,17 +22,47 @@ interface Bip32 {
private: number;
}
+/**
+ * Represents the Bitcoin network configuration.
+ */
export const bitcoin: Network = {
+ /**
+ * The message prefix used for signing Bitcoin messages.
+ */
messagePrefix: '\x18Bitcoin Signed Message:\n',
+ /**
+ * The Bech32 prefix used for Bitcoin addresses.
+ */
bech32: 'bc',
+ /**
+ * The BIP32 key prefixes for Bitcoin.
+ */
bip32: {
+ /**
+ * The public key prefix for BIP32 extended public keys.
+ */
public: 0x0488b21e,
+ /**
+ * The private key prefix for BIP32 extended private keys.
+ */
private: 0x0488ade4,
},
+ /**
+ * The prefix for Bitcoin public key hashes.
+ */
pubKeyHash: 0x00,
+ /**
+ * The prefix for Bitcoin script hashes.
+ */
scriptHash: 0x05,
+ /**
+ * The prefix for Bitcoin Wallet Import Format (WIF) private keys.
+ */
wif: 0x80,
};
+/**
+ * Represents the regtest network configuration.
+ */
export const regtest: Network = {
messagePrefix: '\x18Bitcoin Signed Message:\n',
bech32: 'bcrt',
@@ -36,6 +74,9 @@ export const regtest: Network = {
scriptHash: 0xc4,
wif: 0xef,
};
+/**
+ * Represents the testnet network configuration.
+ */
export const testnet: Network = {
messagePrefix: '\x18Bitcoin Signed Message:\n',
bech32: 'tb',
diff --git a/ts_src/ops.ts b/ts_src/ops.ts
index 8e2c41c11..99de223cb 100644
--- a/ts_src/ops.ts
+++ b/ts_src/ops.ts
@@ -1,141 +1,139 @@
-const OPS: { [key: string]: number } = {
- OP_FALSE: 0,
- OP_0: 0,
- OP_PUSHDATA1: 76,
- OP_PUSHDATA2: 77,
- OP_PUSHDATA4: 78,
- OP_1NEGATE: 79,
- OP_RESERVED: 80,
- OP_TRUE: 81,
- OP_1: 81,
- OP_2: 82,
- OP_3: 83,
- OP_4: 84,
- OP_5: 85,
- OP_6: 86,
- OP_7: 87,
- OP_8: 88,
- OP_9: 89,
- OP_10: 90,
- OP_11: 91,
- OP_12: 92,
- OP_13: 93,
- OP_14: 94,
- OP_15: 95,
- OP_16: 96,
+// Define OPS enum
+enum OPS {
+ OP_FALSE = 0,
+ OP_0 = OPS.OP_FALSE, // Avoid duplicate value
+ OP_PUSHDATA1 = 76,
+ OP_PUSHDATA2 = 77,
+ OP_PUSHDATA4 = 78,
+ OP_1NEGATE = 79,
+ OP_RESERVED = 80,
+ OP_TRUE = 81,
+ OP_1 = OPS.OP_TRUE, // Avoid duplicate value
+ OP_2 = 82,
+ OP_3 = 83,
+ OP_4 = 84,
+ OP_5 = 85,
+ OP_6 = 86,
+ OP_7 = 87,
+ OP_8 = 88,
+ OP_9 = 89,
+ OP_10 = 90,
+ OP_11 = 91,
+ OP_12 = 92,
+ OP_13 = 93,
+ OP_14 = 94,
+ OP_15 = 95,
+ OP_16 = 96,
- OP_NOP: 97,
- OP_VER: 98,
- OP_IF: 99,
- OP_NOTIF: 100,
- OP_VERIF: 101,
- OP_VERNOTIF: 102,
- OP_ELSE: 103,
- OP_ENDIF: 104,
- OP_VERIFY: 105,
- OP_RETURN: 106,
+ OP_NOP = 97,
+ OP_VER = 98,
+ OP_IF = 99,
+ OP_NOTIF = 100,
+ OP_VERIF = 101,
+ OP_VERNOTIF = 102,
+ OP_ELSE = 103,
+ OP_ENDIF = 104,
+ OP_VERIFY = 105,
+ OP_RETURN = 106,
- OP_TOALTSTACK: 107,
- OP_FROMALTSTACK: 108,
- OP_2DROP: 109,
- OP_2DUP: 110,
- OP_3DUP: 111,
- OP_2OVER: 112,
- OP_2ROT: 113,
- OP_2SWAP: 114,
- OP_IFDUP: 115,
- OP_DEPTH: 116,
- OP_DROP: 117,
- OP_DUP: 118,
- OP_NIP: 119,
- OP_OVER: 120,
- OP_PICK: 121,
- OP_ROLL: 122,
- OP_ROT: 123,
- OP_SWAP: 124,
- OP_TUCK: 125,
+ OP_TOALTSTACK = 107,
+ OP_FROMALTSTACK = 108,
+ OP_2DROP = 109,
+ OP_2DUP = 110,
+ OP_3DUP = 111,
+ OP_2OVER = 112,
+ OP_2ROT = 113,
+ OP_2SWAP = 114,
+ OP_IFDUP = 115,
+ OP_DEPTH = 116,
+ OP_DROP = 117,
+ OP_DUP = 118,
+ OP_NIP = 119,
+ OP_OVER = 120,
+ OP_PICK = 121,
+ OP_ROLL = 122,
+ OP_ROT = 123,
+ OP_SWAP = 124,
+ OP_TUCK = 125,
- OP_CAT: 126,
- OP_SUBSTR: 127,
- OP_LEFT: 128,
- OP_RIGHT: 129,
- OP_SIZE: 130,
+ OP_CAT = 126,
+ OP_SUBSTR = 127,
+ OP_LEFT = 128,
+ OP_RIGHT = 129,
+ OP_SIZE = 130,
- OP_INVERT: 131,
- OP_AND: 132,
- OP_OR: 133,
- OP_XOR: 134,
- OP_EQUAL: 135,
- OP_EQUALVERIFY: 136,
- OP_RESERVED1: 137,
- OP_RESERVED2: 138,
+ OP_INVERT = 131,
+ OP_AND = 132,
+ OP_OR = 133,
+ OP_XOR = 134,
+ OP_EQUAL = 135,
+ OP_EQUALVERIFY = 136,
+ OP_RESERVED1 = 137,
+ OP_RESERVED2 = 138,
- OP_1ADD: 139,
- OP_1SUB: 140,
- OP_2MUL: 141,
- OP_2DIV: 142,
- OP_NEGATE: 143,
- OP_ABS: 144,
- OP_NOT: 145,
- OP_0NOTEQUAL: 146,
- OP_ADD: 147,
- OP_SUB: 148,
- OP_MUL: 149,
- OP_DIV: 150,
- OP_MOD: 151,
- OP_LSHIFT: 152,
- OP_RSHIFT: 153,
+ OP_1ADD = 139,
+ OP_1SUB = 140,
+ OP_2MUL = 141,
+ OP_2DIV = 142,
+ OP_NEGATE = 143,
+ OP_ABS = 144,
+ OP_NOT = 145,
+ OP_0NOTEQUAL = 146,
+ OP_ADD = 147,
+ OP_SUB = 148,
+ OP_MUL = 149,
+ OP_DIV = 150,
+ OP_MOD = 151,
+ OP_LSHIFT = 152,
+ OP_RSHIFT = 153,
- OP_BOOLAND: 154,
- OP_BOOLOR: 155,
- OP_NUMEQUAL: 156,
- OP_NUMEQUALVERIFY: 157,
- OP_NUMNOTEQUAL: 158,
- OP_LESSTHAN: 159,
- OP_GREATERTHAN: 160,
- OP_LESSTHANOREQUAL: 161,
- OP_GREATERTHANOREQUAL: 162,
- OP_MIN: 163,
- OP_MAX: 164,
+ OP_BOOLAND = 154,
+ OP_BOOLOR = 155,
+ OP_NUMEQUAL = 156,
+ OP_NUMEQUALVERIFY = 157,
+ OP_NUMNOTEQUAL = 158,
+ OP_LESSTHAN = 159,
+ OP_GREATERTHAN = 160,
+ OP_LESSTHANOREQUAL = 161,
+ OP_GREATERTHANOREQUAL = 162,
+ OP_MIN = 163,
+ OP_MAX = 164,
- OP_WITHIN: 165,
+ OP_WITHIN = 165,
- OP_RIPEMD160: 166,
- OP_SHA1: 167,
- OP_SHA256: 168,
- OP_HASH160: 169,
- OP_HASH256: 170,
- OP_CODESEPARATOR: 171,
- OP_CHECKSIG: 172,
- OP_CHECKSIGVERIFY: 173,
- OP_CHECKMULTISIG: 174,
- OP_CHECKMULTISIGVERIFY: 175,
+ OP_RIPEMD160 = 166,
+ OP_SHA1 = 167,
+ OP_SHA256 = 168,
+ OP_HASH160 = 169,
+ OP_HASH256 = 170,
+ OP_CODESEPARATOR = 171,
+ OP_CHECKSIG = 172,
+ OP_CHECKSIGVERIFY = 173,
+ OP_CHECKMULTISIG = 174,
+ OP_CHECKMULTISIGVERIFY = 175,
- OP_NOP1: 176,
+ OP_NOP1 = 176,
- OP_NOP2: 177,
- OP_CHECKLOCKTIMEVERIFY: 177,
+ OP_CHECKLOCKTIMEVERIFY = 177, // Alias: OP_NOP2
+ OP_NOP2 = OPS.OP_CHECKLOCKTIMEVERIFY,
- OP_NOP3: 178,
- OP_CHECKSEQUENCEVERIFY: 178,
+ OP_CHECKSEQUENCEVERIFY = 178, // Alias: OP_NOP3
+ OP_NOP3 = OPS.OP_CHECKSEQUENCEVERIFY,
- OP_NOP4: 179,
- OP_NOP5: 180,
- OP_NOP6: 181,
- OP_NOP7: 182,
- OP_NOP8: 183,
- OP_NOP9: 184,
- OP_NOP10: 185,
+ OP_NOP4 = 179,
+ OP_NOP5 = 180,
+ OP_NOP6 = 181,
+ OP_NOP7 = 182,
+ OP_NOP8 = 183,
+ OP_NOP9 = 184,
+ OP_NOP10 = 185,
- OP_PUBKEYHASH: 253,
- OP_PUBKEY: 254,
- OP_INVALIDOPCODE: 255,
-};
+ OP_CHECKSIGADD = 186,
-const REVERSE_OPS: { [key: number]: string } = {};
-for (const op of Object.keys(OPS)) {
- const code = OPS[op];
- REVERSE_OPS[code] = op;
+ OP_PUBKEYHASH = 253,
+ OP_PUBKEY = 254,
+ OP_INVALIDOPCODE = 255,
}
-export { OPS, REVERSE_OPS };
+// Export modules
+export { OPS };
diff --git a/ts_src/payments/bip341.ts b/ts_src/payments/bip341.ts
new file mode 100644
index 000000000..5e6b4e7e5
--- /dev/null
+++ b/ts_src/payments/bip341.ts
@@ -0,0 +1,189 @@
+import { getEccLib } from '../ecc_lib.js';
+import * as bcrypto from '../crypto.js';
+
+import { varuint } from '../bufferutils.js';
+import { Tapleaf, Taptree, isTapleaf } from '../types.js';
+import * as tools from 'uint8array-tools';
+
+export const LEAF_VERSION_TAPSCRIPT = 0xc0;
+export const MAX_TAPTREE_DEPTH = 128;
+
+interface HashLeaf {
+ hash: Uint8Array;
+}
+
+interface HashBranch {
+ hash: Uint8Array;
+ left: HashTree;
+ right: HashTree;
+}
+
+interface TweakedPublicKey {
+ parity: number;
+ x: Uint8Array;
+}
+
+const isHashBranch = (ht: HashTree): ht is HashBranch =>
+ 'left' in ht && 'right' in ht;
+
+/**
+ * Binary tree representing leaf, branch, and root node hashes of a Taptree.
+ * Each node contains a hash, and potentially left and right branch hashes.
+ * This tree is used for 2 purposes: Providing the root hash for tweaking,
+ * and calculating merkle inclusion proofs when constructing a control block.
+ */
+export type HashTree = HashLeaf | HashBranch;
+
+/**
+ * Calculates the root hash from a given control block and leaf hash.
+ * @param controlBlock - The control block buffer.
+ * @param leafHash - The leaf hash buffer.
+ * @returns The root hash buffer.
+ * @throws {TypeError} If the control block length is less than 33.
+ */
+export function rootHashFromPath(
+ controlBlock: Uint8Array,
+ leafHash: Uint8Array,
+): Uint8Array {
+ if (controlBlock.length < 33)
+ throw new TypeError(
+ `The control-block length is too small. Got ${controlBlock.length}, expected min 33.`,
+ );
+ const m = (controlBlock.length - 33) / 32;
+
+ let kj = leafHash;
+ for (let j = 0; j < m; j++) {
+ const ej = controlBlock.slice(33 + 32 * j, 65 + 32 * j);
+ if (tools.compare(kj, ej) < 0) {
+ kj = tapBranchHash(kj, ej);
+ } else {
+ kj = tapBranchHash(ej, kj);
+ }
+ }
+
+ return kj;
+}
+
+/**
+ * Build a hash tree of merkle nodes from the scripts binary tree.
+ * @param scriptTree - the tree of scripts to pairwise hash.
+ */
+export function toHashTree(scriptTree: Taptree): HashTree {
+ if (isTapleaf(scriptTree)) return { hash: tapleafHash(scriptTree) };
+
+ const hashes = [toHashTree(scriptTree[0]), toHashTree(scriptTree[1])];
+ // hashes.sort((a, b) => a.hash.compare(b.hash));
+ hashes.sort((a, b) => tools.compare(a.hash, b.hash));
+ const [left, right] = hashes;
+
+ return {
+ hash: tapBranchHash(left.hash, right.hash),
+ left,
+ right,
+ };
+}
+
+/**
+ * Given a HashTree, finds the path from a particular hash to the root.
+ * @param node - the root of the tree
+ * @param hash - the hash to search for
+ * @returns - array of sibling hashes, from leaf (inclusive) to root
+ * (exclusive) needed to prove inclusion of the specified hash. undefined if no
+ * path is found
+ */
+export function findScriptPath(
+ node: HashTree,
+ hash: Uint8Array,
+): Uint8Array[] | undefined {
+ if (isHashBranch(node)) {
+ const leftPath = findScriptPath(node.left, hash);
+ if (leftPath !== undefined) return [...leftPath, node.right.hash];
+
+ const rightPath = findScriptPath(node.right, hash);
+ if (rightPath !== undefined) return [...rightPath, node.left.hash];
+ } else if (tools.compare(node.hash, hash) === 0) {
+ return [];
+ }
+
+ return undefined;
+}
+/**
+ * Calculates the tapleaf hash for a given Tapleaf object.
+ * @param leaf - The Tapleaf object to calculate the hash for.
+ * @returns The tapleaf hash as a Buffer.
+ */
+export function tapleafHash(leaf: Tapleaf): Uint8Array {
+ const version = leaf.version || LEAF_VERSION_TAPSCRIPT;
+ return bcrypto.taggedHash(
+ 'TapLeaf',
+ tools.concat([Uint8Array.from([version]), serializeScript(leaf.output)]),
+ );
+}
+
+/**
+ * Computes the taproot tweak hash for a given public key and optional hash.
+ * If a hash is provided, the public key and hash are concatenated before computing the hash.
+ * If no hash is provided, only the public key is used to compute the hash.
+ *
+ * @param pubKey - The public key buffer.
+ * @param h - The optional hash buffer.
+ * @returns The taproot tweak hash.
+ */
+export function tapTweakHash(
+ pubKey: Uint8Array,
+ h: Uint8Array | undefined,
+): Uint8Array {
+ return bcrypto.taggedHash(
+ 'TapTweak',
+ tools.concat(h ? [pubKey, h] : [pubKey]),
+ );
+}
+
+/**
+ * Tweak a public key with a given tweak hash.
+ * @param pubKey - The public key to be tweaked.
+ * @param h - The tweak hash.
+ * @returns The tweaked public key or null if the input is invalid.
+ */
+export function tweakKey(
+ pubKey: Uint8Array,
+ h: Uint8Array | undefined,
+): TweakedPublicKey | null {
+ if (!(pubKey instanceof Uint8Array)) return null;
+ if (pubKey.length !== 32) return null;
+ if (h && h.length !== 32) return null;
+
+ const tweakHash = tapTweakHash(pubKey, h);
+
+ const res = getEccLib().xOnlyPointAddTweak(pubKey, tweakHash);
+ if (!res || res.xOnlyPubkey === null) return null;
+
+ return {
+ parity: res.parity,
+ x: Uint8Array.from(res.xOnlyPubkey),
+ };
+}
+/**
+ * Computes the TapBranch hash by concatenating two buffers and applying the 'TapBranch' tagged hash algorithm.
+ *
+ * @param a - The first buffer.
+ * @param b - The second buffer.
+ * @returns The TapBranch hash of the concatenated buffers.
+ */
+function tapBranchHash(a: Uint8Array, b: Uint8Array): Uint8Array {
+ return bcrypto.taggedHash('TapBranch', tools.concat([a, b]));
+}
+
+/**
+ * Serializes a script by encoding its length as a varint and concatenating it with the script.
+ *
+ * @param s - The script to be serialized.
+ * @returns The serialized script as a Buffer.
+ */
+function serializeScript(s: Uint8Array): Uint8Array {
+ /* global BigInt */
+ const varintLen = varuint.encodingLength(s.length);
+ const buffer = new Uint8Array(varintLen);
+ varuint.encode(s.length, buffer);
+ return tools.concat([buffer, s]);
+}
diff --git a/ts_src/payments/embed.ts b/ts_src/payments/embed.ts
index c479b899a..dccbc5777 100644
--- a/ts_src/payments/embed.ts
+++ b/ts_src/payments/embed.ts
@@ -1,30 +1,32 @@
-import { bitcoin as BITCOIN_NETWORK } from '../networks';
-import * as bscript from '../script';
-import { typeforce as typef } from '../types';
-import { Payment, PaymentOpts, Stack } from './index';
-import * as lazy from './lazy';
+import { bitcoin as BITCOIN_NETWORK } from '../networks.js';
+import * as bscript from '../script.js';
+import { stacksEqual, BufferSchema } from '../types.js';
+import { Payment, PaymentOpts, Stack } from './index.js';
+import * as lazy from './lazy.js';
+import * as v from 'valibot';
const OPS = bscript.OPS;
-function stacksEqual(a: Buffer[], b: Buffer[]): boolean {
- if (a.length !== b.length) return false;
-
- return a.every((x, i) => {
- return x.equals(b[i]);
- });
-}
-
// output: OP_RETURN ...
+/**
+ * Embeds data in a Bitcoin payment.
+ * @param a - The payment object.
+ * @param opts - Optional payment options.
+ * @returns The modified payment object.
+ * @throws {TypeError} If there is not enough data or if the output is invalid.
+ */
export function p2data(a: Payment, opts?: PaymentOpts): Payment {
if (!a.data && !a.output) throw new TypeError('Not enough data');
opts = Object.assign({ validate: true }, opts || {});
- typef(
- {
- network: typef.maybe(typef.Object),
- output: typef.maybe(typef.Buffer),
- data: typef.maybe(typef.arrayOf(typef.Buffer)),
- },
+ v.parse(
+ v.partial(
+ v.object({
+ network: v.object({}),
+ output: BufferSchema,
+ data: v.array(BufferSchema),
+ }),
+ ),
a,
);
@@ -46,10 +48,9 @@ export function p2data(a: Payment, opts?: PaymentOpts): Payment {
const chunks = bscript.decompile(a.output);
if (chunks![0] !== OPS.OP_RETURN)
throw new TypeError('Output is invalid');
- if (!chunks!.slice(1).every(typef.Buffer))
+ if (!chunks!.slice(1).every(chunk => v.is(BufferSchema, chunk)))
throw new TypeError('Output is invalid');
-
- if (a.data && !stacksEqual(a.data, o.data as Buffer[]))
+ if (a.data && !stacksEqual(a.data, o.data as Uint8Array[]))
throw new TypeError('Data mismatch');
}
}
diff --git a/ts_src/payments/index.ts b/ts_src/payments/index.ts
index 4b7f1117e..a9d79981f 100644
--- a/ts_src/payments/index.ts
+++ b/ts_src/payments/index.ts
@@ -1,28 +1,48 @@
-import { Network } from '../networks';
-import { p2data as embed } from './embed';
-import { p2ms } from './p2ms';
-import { p2pk } from './p2pk';
-import { p2pkh } from './p2pkh';
-import { p2sh } from './p2sh';
-import { p2wpkh } from './p2wpkh';
-import { p2wsh } from './p2wsh';
+/**
+ * Provides functionality for creating and managing Bitcoin payment objects.
+ *
+ * This module supports multiple Bitcoin address types for payments, including:
+ * - P2PKH (Pay-to-PubKey-Hash)
+ * - P2SH (Pay-to-Script-Hash)
+ * - P2WPKH (Pay-to-Witness-PubKey-Hash)
+ * - P2WSH (Pay-to-Witness-Script-Hash)
+ * - P2TR (Taproot)
+ *
+ * The `Payment` interface defines the structure of a payment object used for constructing various
+ * payment types, with fields for signatures, public keys, redeem scripts, and more.
+ *
+ * @packageDocumentation
+ */
+import { Network } from '../networks.js';
+import { Taptree } from '../types.js';
+import { p2data as embed } from './embed.js';
+import { p2ms } from './p2ms.js';
+import { p2pk } from './p2pk.js';
+import { p2pkh } from './p2pkh.js';
+import { p2sh } from './p2sh.js';
+import { p2wpkh } from './p2wpkh.js';
+import { p2wsh } from './p2wsh.js';
+import { p2tr } from './p2tr.js';
export interface Payment {
name?: string;
network?: Network;
- output?: Buffer;
- data?: Buffer[];
+ output?: Uint8Array;
+ data?: Uint8Array[];
m?: number;
n?: number;
- pubkeys?: Buffer[];
- input?: Buffer;
- signatures?: Buffer[];
- pubkey?: Buffer;
- signature?: Buffer;
+ pubkeys?: Uint8Array[];
+ input?: Uint8Array;
+ signatures?: Uint8Array[];
+ internalPubkey?: Uint8Array;
+ pubkey?: Uint8Array;
+ signature?: Uint8Array;
address?: string;
- hash?: Buffer;
+ hash?: Uint8Array;
redeem?: Payment;
- witness?: Buffer[];
+ redeemVersion?: number;
+ scriptTree?: Taptree;
+ witness?: Uint8Array[];
}
export type PaymentCreator = (a: Payment, opts?: PaymentOpts) => Payment;
@@ -34,11 +54,11 @@ export interface PaymentOpts {
allowIncomplete?: boolean;
}
-export type StackElement = Buffer | number;
+export type StackElement = Uint8Array | number;
export type Stack = StackElement[];
export type StackFunction = () => Stack;
-export { embed, p2ms, p2pk, p2pkh, p2sh, p2wpkh, p2wsh };
+export { embed, p2ms, p2pk, p2pkh, p2sh, p2wpkh, p2wsh, p2tr };
// TODO
// witness commitment
diff --git a/ts_src/payments/lazy.ts b/ts_src/payments/lazy.ts
index 1df181fd9..22906d9ec 100644
--- a/ts_src/payments/lazy.ts
+++ b/ts_src/payments/lazy.ts
@@ -1,4 +1,4 @@
-export function prop(object: {}, name: string, f: () => any): void {
+export function prop(object: object, name: string, f: () => any): void {
Object.defineProperty(object, name, {
configurable: true,
enumerable: true,
diff --git a/ts_src/payments/p2ms.ts b/ts_src/payments/p2ms.ts
index eaa144069..862448a6a 100644
--- a/ts_src/payments/p2ms.ts
+++ b/ts_src/payments/p2ms.ts
@@ -1,22 +1,39 @@
-import { bitcoin as BITCOIN_NETWORK } from '../networks';
-import * as bscript from '../script';
-import { isPoint, typeforce as typef } from '../types';
-import { Payment, PaymentOpts, Stack } from './index';
-import * as lazy from './lazy';
+import { bitcoin as BITCOIN_NETWORK } from '../networks.js';
+import * as bscript from '../script.js';
+import * as scriptNumber from '../script_number.js';
+import { BufferSchema, isPoint, stacksEqual } from '../types.js';
+import { Payment, PaymentOpts, Stack } from './index.js';
+import * as lazy from './lazy.js';
+import * as v from 'valibot';
const OPS = bscript.OPS;
const OP_INT_BASE = OPS.OP_RESERVED; // OP_1 - 1
-function stacksEqual(a: Buffer[], b: Buffer[]): boolean {
- if (a.length !== b.length) return false;
-
- return a.every((x, i) => {
- return x.equals(b[i]);
- });
+function encodeSmallOrScriptNum(n: number): number | Uint8Array {
+ return n <= 16 ? OP_INT_BASE + n : scriptNumber.encode(n);
+}
+function decodeSmallOrScriptNum(chunk: number | Uint8Array): number {
+ if (typeof chunk === 'number') {
+ const val = chunk - OP_INT_BASE;
+ if (val < 1 || val > 16)
+ throw new TypeError(`Invalid opcode: expected OP_1–OP_16, got ${chunk}`);
+ return val;
+ } else return scriptNumber.decode(chunk);
+}
+function isSmallOrScriptNum(chunk: number | Uint8Array): boolean {
+ if (typeof chunk === 'number')
+ return chunk - OP_INT_BASE >= 1 && chunk - OP_INT_BASE <= 16;
+ else return Number.isInteger(scriptNumber.decode(chunk));
}
-
// input: OP_0 [signatures ...]
// output: m [pubKeys ...] n OP_CHECKMULTISIG
+/**
+ * Represents a function that creates a Pay-to-Multisig (P2MS) payment object.
+ * @param a - The payment object.
+ * @param opts - Optional payment options.
+ * @returns The created payment object.
+ * @throws {TypeError} If the provided data is not valid.
+ */
export function p2ms(a: Payment, opts?: PaymentOpts): Payment {
if (
!a.input &&
@@ -27,24 +44,32 @@ export function p2ms(a: Payment, opts?: PaymentOpts): Payment {
throw new TypeError('Not enough data');
opts = Object.assign({ validate: true }, opts || {});
- function isAcceptableSignature(x: Buffer | number): boolean {
+ function isAcceptableSignature(x: Uint8Array | number): boolean {
return (
- bscript.isCanonicalScriptSignature(x as Buffer) ||
+ bscript.isCanonicalScriptSignature(x as Uint8Array) ||
(opts!.allowIncomplete && (x as number) === OPS.OP_0) !== undefined
);
}
- typef(
- {
- network: typef.maybe(typef.Object),
- m: typef.maybe(typef.Number),
- n: typef.maybe(typef.Number),
- output: typef.maybe(typef.Buffer),
- pubkeys: typef.maybe(typef.arrayOf(isPoint)),
-
- signatures: typef.maybe(typef.arrayOf(isAcceptableSignature)),
- input: typef.maybe(typef.Buffer),
- },
+ v.parse(
+ v.partial(
+ v.object({
+ network: v.object({}),
+ m: v.number(),
+ n: v.number(),
+ output: BufferSchema,
+ pubkeys: v.array(
+ v.custom(isPoint as (input: unknown) => boolean),
+ 'Received invalid pubkey',
+ ),
+
+ signatures: v.array(
+ v.custom(isAcceptableSignature as (input: unknown) => boolean),
+ 'Expected signature to be of type isAcceptableSignature',
+ ),
+ input: BufferSchema,
+ }),
+ ),
a,
);
@@ -53,13 +78,14 @@ export function p2ms(a: Payment, opts?: PaymentOpts): Payment {
let chunks: Stack = [];
let decoded = false;
- function decode(output: Buffer | Stack): void {
+ function decode(output: Uint8Array | Stack): void {
if (decoded) return;
decoded = true;
chunks = bscript.decompile(output) as Stack;
- o.m = (chunks[0] as number) - OP_INT_BASE;
- o.n = (chunks[chunks.length - 2] as number) - OP_INT_BASE;
- o.pubkeys = chunks.slice(1, -2) as Buffer[];
+ if (chunks.length < 3) throw new TypeError('Output is invalid');
+ o.m = decodeSmallOrScriptNum(chunks[0]);
+ o.n = decodeSmallOrScriptNum(chunks[chunks.length - 2]);
+ o.pubkeys = chunks.slice(1, -2) as Uint8Array[];
}
lazy.prop(o, 'output', () => {
@@ -68,9 +94,9 @@ export function p2ms(a: Payment, opts?: PaymentOpts): Payment {
if (!a.pubkeys) return;
return bscript.compile(
([] as Stack).concat(
- OP_INT_BASE + a.m,
+ encodeSmallOrScriptNum(a.m),
a.pubkeys,
- OP_INT_BASE + o.n,
+ encodeSmallOrScriptNum(o.n),
OPS.OP_CHECKMULTISIG,
),
);
@@ -110,13 +136,14 @@ export function p2ms(a: Payment, opts?: PaymentOpts): Payment {
if (opts.validate) {
if (a.output) {
decode(a.output);
- if (!typef.Number(chunks[0])) throw new TypeError('Output is invalid');
- if (!typef.Number(chunks[chunks.length - 2]))
+ if (!isSmallOrScriptNum(chunks[0]))
+ throw new TypeError('Output is invalid');
+ if (!isSmallOrScriptNum(chunks[chunks.length - 2]))
throw new TypeError('Output is invalid');
if (chunks[chunks.length - 1] !== OPS.OP_CHECKMULTISIG)
throw new TypeError('Output is invalid');
- if (o.m! <= 0 || o.n! > 16 || o.m! > o.n! || o.n !== chunks.length - 3)
+ if (o.m! <= 0 || o.n! > 20 || o.m! > o.n! || o.n !== chunks.length - 3)
throw new TypeError('Output is invalid');
if (!o.pubkeys!.every(x => isPoint(x)))
throw new TypeError('Output is invalid');
diff --git a/ts_src/payments/p2pk.ts b/ts_src/payments/p2pk.ts
index 7273f53fb..5726e2052 100644
--- a/ts_src/payments/p2pk.ts
+++ b/ts_src/payments/p2pk.ts
@@ -1,26 +1,44 @@
-import { bitcoin as BITCOIN_NETWORK } from '../networks';
-import * as bscript from '../script';
-import { isPoint, typeforce as typef } from '../types';
-import { Payment, PaymentOpts, StackFunction } from './index';
-import * as lazy from './lazy';
+import { bitcoin as BITCOIN_NETWORK } from '../networks.js';
+import * as bscript from '../script.js';
+import { BufferSchema, isPoint } from '../types.js';
+import { Payment, PaymentOpts, StackFunction } from './index.js';
+import * as lazy from './lazy.js';
+import * as tools from 'uint8array-tools';
+import * as v from 'valibot';
const OPS = bscript.OPS;
// input: {signature}
// output: {pubKey} OP_CHECKSIG
+/**
+ * Creates a pay-to-public-key (P2PK) payment object.
+ *
+ * @param a - The payment object containing the necessary data.
+ * @param opts - Optional payment options.
+ * @returns The P2PK payment object.
+ * @throws {TypeError} If the required data is not provided or if the data is invalid.
+ */
export function p2pk(a: Payment, opts?: PaymentOpts): Payment {
if (!a.input && !a.output && !a.pubkey && !a.input && !a.signature)
throw new TypeError('Not enough data');
opts = Object.assign({ validate: true }, opts || {});
- typef(
- {
- network: typef.maybe(typef.Object),
- output: typef.maybe(typef.Buffer),
- pubkey: typef.maybe(isPoint),
+ v.parse(
+ v.partial(
+ v.object({
+ network: v.object({}),
+ output: BufferSchema,
+ pubkey: v.custom(
+ isPoint as (input: unknown) => boolean,
+ 'invalid pubkey',
+ ),
- signature: typef.maybe(bscript.isCanonicalScriptSignature),
- input: typef.maybe(typef.Buffer),
- },
+ signature: v.custom(
+ bscript.isCanonicalScriptSignature as (input: unknown) => boolean,
+ 'Expected signature to be of type isCanonicalScriptSignature',
+ ),
+ input: BufferSchema,
+ }),
+ ),
a,
);
@@ -41,7 +59,7 @@ export function p2pk(a: Payment, opts?: PaymentOpts): Payment {
});
lazy.prop(o, 'signature', () => {
if (!a.input) return;
- return _chunks()[0] as Buffer;
+ return _chunks()[0] as Uint8Array;
});
lazy.prop(o, 'input', () => {
if (!a.signature) return;
@@ -58,12 +76,12 @@ export function p2pk(a: Payment, opts?: PaymentOpts): Payment {
if (a.output[a.output.length - 1] !== OPS.OP_CHECKSIG)
throw new TypeError('Output is invalid');
if (!isPoint(o.pubkey)) throw new TypeError('Output pubkey is invalid');
- if (a.pubkey && !a.pubkey.equals(o.pubkey!))
+ if (a.pubkey && tools.compare(a.pubkey, o.pubkey!) !== 0)
throw new TypeError('Pubkey mismatch');
}
if (a.signature) {
- if (a.input && !a.input.equals(o.input!))
+ if (a.input && tools.compare(a.input, o.input!) !== 0)
throw new TypeError('Signature mismatch');
}
diff --git a/ts_src/payments/p2pkh.ts b/ts_src/payments/p2pkh.ts
index 3b5bd6f79..696e09714 100644
--- a/ts_src/payments/p2pkh.ts
+++ b/ts_src/payments/p2pkh.ts
@@ -1,36 +1,56 @@
-import * as bcrypto from '../crypto';
-import { bitcoin as BITCOIN_NETWORK } from '../networks';
-import * as bscript from '../script';
-import { isPoint, typeforce as typef } from '../types';
-import { Payment, PaymentOpts, StackFunction } from './index';
-import * as lazy from './lazy';
-import * as bs58check from 'bs58check';
+import * as bcrypto from '../crypto.js';
+import { bitcoin as BITCOIN_NETWORK } from '../networks.js';
+import * as bscript from '../script.js';
+import {
+ isPoint,
+ Hash160bitSchema,
+ NBufferSchemaFactory,
+ BufferSchema,
+} from '../types.js';
+import { Payment, PaymentOpts, StackFunction } from './index.js';
+import * as lazy from './lazy.js';
+import bs58check from 'bs58check';
+import * as tools from 'uint8array-tools';
+import * as v from 'valibot';
+
const OPS = bscript.OPS;
// input: {signature} {pubkey}
// output: OP_DUP OP_HASH160 {hash160(pubkey)} OP_EQUALVERIFY OP_CHECKSIG
+/**
+ * Creates a Pay-to-Public-Key-Hash (P2PKH) payment object.
+ *
+ * @param a - The payment object containing the necessary data.
+ * @param opts - Optional payment options.
+ * @returns The P2PKH payment object.
+ * @throws {TypeError} If the required data is not provided or if the data is invalid.
+ */
export function p2pkh(a: Payment, opts?: PaymentOpts): Payment {
if (!a.address && !a.hash && !a.output && !a.pubkey && !a.input)
throw new TypeError('Not enough data');
opts = Object.assign({ validate: true }, opts || {});
- typef(
- {
- network: typef.maybe(typef.Object),
- address: typef.maybe(typef.String),
- hash: typef.maybe(typef.BufferN(20)),
- output: typef.maybe(typef.BufferN(25)),
-
- pubkey: typef.maybe(isPoint),
- signature: typef.maybe(bscript.isCanonicalScriptSignature),
- input: typef.maybe(typef.Buffer),
- },
+ v.parse(
+ v.partial(
+ v.object({
+ network: v.object({}),
+ address: v.string(),
+ hash: Hash160bitSchema,
+ output: NBufferSchemaFactory(25),
+
+ pubkey: v.custom(isPoint as (input: unknown) => boolean),
+ signature: v.custom(
+ bscript.isCanonicalScriptSignature as (input: unknown) => boolean,
+ ),
+ input: BufferSchema,
+ }),
+ ),
a,
);
const _address = lazy.value(() => {
const payload = bs58check.decode(a.address!);
- const version = payload.readUInt8(0);
+ const version = tools.readUInt8(payload, 0);
const hash = payload.slice(1);
return { version, hash };
});
@@ -44,9 +64,9 @@ export function p2pkh(a: Payment, opts?: PaymentOpts): Payment {
lazy.prop(o, 'address', () => {
if (!o.hash) return;
- const payload = Buffer.allocUnsafe(21);
- payload.writeUInt8(network.pubKeyHash, 0);
- o.hash.copy(payload, 1);
+ const payload = new Uint8Array(21);
+ tools.writeUInt8(payload, 0, network.pubKeyHash);
+ payload.set(o.hash, 1);
return bs58check.encode(payload);
});
lazy.prop(o, 'hash', () => {
@@ -66,11 +86,11 @@ export function p2pkh(a: Payment, opts?: PaymentOpts): Payment {
});
lazy.prop(o, 'pubkey', () => {
if (!a.input) return;
- return _chunks()[1] as Buffer;
+ return _chunks()[1] as Uint8Array;
});
lazy.prop(o, 'signature', () => {
if (!a.input) return;
- return _chunks()[0] as Buffer;
+ return _chunks()[0] as Uint8Array;
});
lazy.prop(o, 'input', () => {
if (!a.pubkey) return;
@@ -84,7 +104,7 @@ export function p2pkh(a: Payment, opts?: PaymentOpts): Payment {
// extended validation
if (opts.validate) {
- let hash: Buffer = Buffer.from([]);
+ let hash: Uint8Array = Uint8Array.from([]);
if (a.address) {
if (_address().version !== network.pubKeyHash)
throw new TypeError('Invalid version or Network mismatch');
@@ -93,7 +113,7 @@ export function p2pkh(a: Payment, opts?: PaymentOpts): Payment {
}
if (a.hash) {
- if (hash.length > 0 && !hash.equals(a.hash))
+ if (hash.length > 0 && tools.compare(hash, a.hash) !== 0)
throw new TypeError('Hash mismatch');
else hash = a.hash;
}
@@ -110,14 +130,14 @@ export function p2pkh(a: Payment, opts?: PaymentOpts): Payment {
throw new TypeError('Output is invalid');
const hash2 = a.output.slice(3, 23);
- if (hash.length > 0 && !hash.equals(hash2))
+ if (hash.length > 0 && tools.compare(hash, hash2) !== 0)
throw new TypeError('Hash mismatch');
else hash = hash2;
}
if (a.pubkey) {
const pkh = bcrypto.hash160(a.pubkey);
- if (hash.length > 0 && !hash.equals(pkh))
+ if (hash.length > 0 && tools.compare(hash, pkh) !== 0)
throw new TypeError('Hash mismatch');
else hash = pkh;
}
@@ -125,17 +145,20 @@ export function p2pkh(a: Payment, opts?: PaymentOpts): Payment {
if (a.input) {
const chunks = _chunks();
if (chunks.length !== 2) throw new TypeError('Input is invalid');
- if (!bscript.isCanonicalScriptSignature(chunks[0] as Buffer))
+ if (!bscript.isCanonicalScriptSignature(chunks[0] as Uint8Array))
throw new TypeError('Input has invalid signature');
if (!isPoint(chunks[1])) throw new TypeError('Input has invalid pubkey');
- if (a.signature && !a.signature.equals(chunks[0] as Buffer))
+ if (
+ a.signature &&
+ tools.compare(a.signature, chunks[0] as Uint8Array) !== 0
+ )
throw new TypeError('Signature mismatch');
- if (a.pubkey && !a.pubkey.equals(chunks[1] as Buffer))
+ if (a.pubkey && tools.compare(a.pubkey, chunks[1] as Uint8Array) !== 0)
throw new TypeError('Pubkey mismatch');
- const pkh = bcrypto.hash160(chunks[1] as Buffer);
- if (hash.length > 0 && !hash.equals(pkh))
+ const pkh = bcrypto.hash160(chunks[1] as Uint8Array);
+ if (hash.length > 0 && tools.compare(hash, pkh) !== 0)
throw new TypeError('Hash mismatch');
}
}
diff --git a/ts_src/payments/p2sh.ts b/ts_src/payments/p2sh.ts
index 9be5a8c8a..cd9dd77eb 100644
--- a/ts_src/payments/p2sh.ts
+++ b/ts_src/payments/p2sh.ts
@@ -1,51 +1,57 @@
-import * as bcrypto from '../crypto';
-import { bitcoin as BITCOIN_NETWORK } from '../networks';
-import * as bscript from '../script';
-import { typeforce as typef } from '../types';
+import * as bcrypto from '../crypto.js';
+import { bitcoin as BITCOIN_NETWORK } from '../networks.js';
+import * as bscript from '../script.js';
+import { BufferSchema, NBufferSchemaFactory, stacksEqual } from '../types.js';
import {
Payment,
PaymentFunction,
PaymentOpts,
Stack,
StackFunction,
-} from './index';
-import * as lazy from './lazy';
-import * as bs58check from 'bs58check';
+} from './index.js';
+import * as lazy from './lazy.js';
+import bs58check from 'bs58check';
+import * as tools from 'uint8array-tools';
+import * as v from 'valibot';
const OPS = bscript.OPS;
-function stacksEqual(a: Buffer[], b: Buffer[]): boolean {
- if (a.length !== b.length) return false;
-
- return a.every((x, i) => {
- return x.equals(b[i]);
- });
-}
-
// input: [redeemScriptSig ...] {redeemScript}
// witness: >
// output: OP_HASH160 {hash160(redeemScript)} OP_EQUAL
+/**
+ * Creates a Pay-to-Script-Hash (P2SH) payment object.
+ *
+ * @param a - The payment object containing the necessary data.
+ * @param opts - Optional payment options.
+ * @returns The P2SH payment object.
+ * @throws {TypeError} If the required data is not provided or if the data is invalid.
+ */
export function p2sh(a: Payment, opts?: PaymentOpts): Payment {
if (!a.address && !a.hash && !a.output && !a.redeem && !a.input)
throw new TypeError('Not enough data');
opts = Object.assign({ validate: true }, opts || {});
- typef(
- {
- network: typef.maybe(typef.Object),
-
- address: typef.maybe(typef.String),
- hash: typef.maybe(typef.BufferN(20)),
- output: typef.maybe(typef.BufferN(23)),
-
- redeem: typef.maybe({
- network: typef.maybe(typef.Object),
- output: typef.maybe(typef.Buffer),
- input: typef.maybe(typef.Buffer),
- witness: typef.maybe(typef.arrayOf(typef.Buffer)),
+ v.parse(
+ v.partial(
+ v.object({
+ network: v.object({}),
+
+ address: v.string(),
+ hash: NBufferSchemaFactory(20),
+ output: NBufferSchemaFactory(23),
+
+ redeem: v.partial(
+ v.object({
+ network: v.object({}),
+ output: BufferSchema,
+ input: BufferSchema,
+ witness: v.array(BufferSchema),
+ }),
+ ),
+ input: BufferSchema,
+ witness: v.array(BufferSchema),
}),
- input: typef.maybe(typef.Buffer),
- witness: typef.maybe(typef.arrayOf(typef.Buffer)),
- },
+ ),
a,
);
@@ -58,32 +64,34 @@ export function p2sh(a: Payment, opts?: PaymentOpts): Payment {
const _address = lazy.value(() => {
const payload = bs58check.decode(a.address!);
- const version = payload.readUInt8(0);
+ const version = tools.readUInt8(payload, 0);
const hash = payload.slice(1);
return { version, hash };
});
const _chunks = lazy.value(() => {
return bscript.decompile(a.input!);
}) as StackFunction;
- const _redeem = lazy.value(
- (): Payment => {
- const chunks = _chunks();
- return {
- network,
- output: chunks[chunks.length - 1] as Buffer,
- input: bscript.compile(chunks.slice(0, -1)),
- witness: a.witness || [],
- };
- },
- ) as PaymentFunction;
+ const _redeem = lazy.value((): Payment => {
+ const chunks = _chunks();
+ const lastChunk = chunks[chunks.length - 1];
+ return {
+ network,
+ output:
+ lastChunk === OPS.OP_FALSE
+ ? Uint8Array.from([])
+ : (lastChunk as Uint8Array),
+ input: bscript.compile(chunks.slice(0, -1)),
+ witness: a.witness || [],
+ };
+ }) as PaymentFunction;
// output dependents
lazy.prop(o, 'address', () => {
if (!o.hash) return;
- const payload = Buffer.allocUnsafe(21);
- payload.writeUInt8(o.network!.scriptHash, 0);
- o.hash.copy(payload, 1);
+ const payload = new Uint8Array(21);
+ tools.writeUInt8(payload, 0, o.network!.scriptHash);
+ payload.set(o.hash, 1);
return bs58check.encode(payload);
});
lazy.prop(o, 'hash', () => {
@@ -123,7 +131,7 @@ export function p2sh(a: Payment, opts?: PaymentOpts): Payment {
});
if (opts.validate) {
- let hash: Buffer = Buffer.from([]);
+ let hash = Uint8Array.from([]);
if (a.address) {
if (_address().version !== network.scriptHash)
throw new TypeError('Invalid version or Network mismatch');
@@ -132,7 +140,7 @@ export function p2sh(a: Payment, opts?: PaymentOpts): Payment {
}
if (a.hash) {
- if (hash.length > 0 && !hash.equals(a.hash))
+ if (hash.length > 0 && tools.compare(hash, a.hash) !== 0)
throw new TypeError('Hash mismatch');
else hash = a.hash;
}
@@ -147,7 +155,7 @@ export function p2sh(a: Payment, opts?: PaymentOpts): Payment {
throw new TypeError('Output is invalid');
const hash2 = a.output.slice(2, 22);
- if (hash.length > 0 && !hash.equals(hash2))
+ if (hash.length > 0 && tools.compare(hash, hash2) !== 0)
throw new TypeError('Hash mismatch');
else hash = hash2;
}
@@ -159,10 +167,18 @@ export function p2sh(a: Payment, opts?: PaymentOpts): Payment {
const decompile = bscript.decompile(redeem.output);
if (!decompile || decompile.length < 1)
throw new TypeError('Redeem.output too short');
+ if (redeem.output.byteLength > 520)
+ throw new TypeError(
+ 'Redeem.output unspendable if larger than 520 bytes',
+ );
+ if (bscript.countNonPushOnlyOPs(decompile) > 201)
+ throw new TypeError(
+ 'Redeem.output unspendable with more than 201 non-push ops',
+ );
// match hash against other sources
const hash2 = bcrypto.hash160(redeem.output);
- if (hash.length > 0 && !hash.equals(hash2))
+ if (hash.length > 0 && tools.compare(hash, hash2) !== 0)
throw new TypeError('Hash mismatch');
else hash = hash2;
}
@@ -184,7 +200,7 @@ export function p2sh(a: Payment, opts?: PaymentOpts): Payment {
if (a.input) {
const chunks = _chunks();
if (!chunks || chunks.length < 1) throw new TypeError('Input too short');
- if (!Buffer.isBuffer(_redeem().output))
+ if (!(_redeem().output instanceof Uint8Array))
throw new TypeError('Input is invalid');
checkRedeem(_redeem());
@@ -195,9 +211,15 @@ export function p2sh(a: Payment, opts?: PaymentOpts): Payment {
throw new TypeError('Network mismatch');
if (a.input) {
const redeem = _redeem();
- if (a.redeem.output && !a.redeem.output.equals(redeem.output!))
+ if (
+ a.redeem.output &&
+ tools.compare(a.redeem.output, redeem.output!) !== 0
+ )
throw new TypeError('Redeem.output mismatch');
- if (a.redeem.input && !a.redeem.input.equals(redeem.input!))
+ if (
+ a.redeem.input &&
+ tools.compare(a.redeem.input, redeem.input!) !== 0
+ )
throw new TypeError('Redeem.input mismatch');
}
diff --git a/ts_src/payments/p2tr.ts b/ts_src/payments/p2tr.ts
new file mode 100644
index 000000000..922064bc0
--- /dev/null
+++ b/ts_src/payments/p2tr.ts
@@ -0,0 +1,339 @@
+import { bitcoin as BITCOIN_NETWORK } from '../networks.js';
+import * as bscript from '../script.js';
+import {
+ isTaptree,
+ TAPLEAF_VERSION_MASK,
+ stacksEqual,
+ NBufferSchemaFactory,
+ BufferSchema,
+} from '../types.js';
+import { getEccLib } from '../ecc_lib.js';
+import {
+ toHashTree,
+ rootHashFromPath,
+ findScriptPath,
+ tapleafHash,
+ tweakKey,
+ LEAF_VERSION_TAPSCRIPT,
+} from './bip341.js';
+import { Payment, PaymentOpts } from './index.js';
+import * as lazy from './lazy.js';
+import { bech32m } from 'bech32';
+import { fromBech32 } from '../address.js';
+import * as tools from 'uint8array-tools';
+import * as v from 'valibot';
+
+const OPS = bscript.OPS;
+const TAPROOT_WITNESS_VERSION = 0x01;
+const ANNEX_PREFIX = 0x50;
+
+/**
+ * Creates a Pay-to-Taproot (P2TR) payment object.
+ *
+ * @param a - The payment object containing the necessary data for P2TR.
+ * @param opts - Optional payment options.
+ * @returns The P2TR payment object.
+ * @throws {TypeError} If the provided data is invalid or insufficient.
+ */
+export function p2tr(a: Payment, opts?: PaymentOpts): Payment {
+ if (
+ !a.address &&
+ !a.output &&
+ !a.pubkey &&
+ !a.internalPubkey &&
+ !(a.witness && a.witness.length > 1)
+ )
+ throw new TypeError('Not enough data');
+
+ opts = Object.assign({ validate: true }, opts || {});
+
+ v.parse(
+ v.partial(
+ v.object({
+ address: v.string(),
+ input: NBufferSchemaFactory(0),
+ network: v.object({}),
+ output: NBufferSchemaFactory(34),
+ internalPubkey: NBufferSchemaFactory(32),
+ hash: NBufferSchemaFactory(32), // merkle root hash, the tweak
+ pubkey: NBufferSchemaFactory(32), // tweaked with `hash` from `internalPubkey`
+ signature: v.union([
+ NBufferSchemaFactory(64),
+ NBufferSchemaFactory(65),
+ ]),
+ witness: v.array(BufferSchema),
+ scriptTree: v.custom(isTaptree, 'Taptree is not of type isTaptree'),
+ redeem: v.partial(
+ v.object({
+ output: BufferSchema, // tapleaf script
+ redeemVersion: v.number(), // tapleaf version
+ witness: v.array(BufferSchema),
+ }),
+ ),
+ redeemVersion: v.number(),
+ }),
+ ),
+ a,
+ );
+
+ const _address = lazy.value(() => {
+ return fromBech32(a.address!);
+ });
+
+ // remove annex if present, ignored by taproot
+ const _witness = lazy.value(() => {
+ if (!a.witness || !a.witness.length) return;
+ if (
+ a.witness.length >= 2 &&
+ a.witness[a.witness.length - 1][0] === ANNEX_PREFIX
+ ) {
+ return a.witness.slice(0, -1);
+ }
+ return a.witness.slice();
+ });
+
+ const _hashTree = lazy.value(() => {
+ if (a.scriptTree) return toHashTree(a.scriptTree);
+ if (a.hash) return { hash: a.hash };
+ return;
+ });
+
+ const network = a.network || BITCOIN_NETWORK;
+ const o: Payment = { name: 'p2tr', network };
+
+ lazy.prop(o, 'address', () => {
+ if (!o.pubkey) return;
+
+ const words = bech32m.toWords(o.pubkey);
+ words.unshift(TAPROOT_WITNESS_VERSION);
+ return bech32m.encode(network.bech32, words);
+ });
+
+ lazy.prop(o, 'hash', () => {
+ const hashTree = _hashTree();
+ if (hashTree) return hashTree.hash;
+ const w = _witness();
+ if (w && w.length > 1) {
+ const controlBlock = w[w.length - 1];
+ const leafVersion = controlBlock[0] & TAPLEAF_VERSION_MASK;
+ const script = w[w.length - 2];
+ const leafHash = tapleafHash({ output: script, version: leafVersion });
+ return rootHashFromPath(controlBlock, leafHash);
+ }
+ return null;
+ });
+ lazy.prop(o, 'output', () => {
+ if (!o.pubkey) return;
+ return bscript.compile([OPS.OP_1, o.pubkey]);
+ });
+ lazy.prop(o, 'redeemVersion', () => {
+ if (a.redeemVersion) return a.redeemVersion;
+ if (
+ a.redeem &&
+ a.redeem.redeemVersion !== undefined &&
+ a.redeem.redeemVersion !== null
+ ) {
+ return a.redeem.redeemVersion;
+ }
+
+ return LEAF_VERSION_TAPSCRIPT;
+ });
+ lazy.prop(o, 'redeem', () => {
+ const witness = _witness(); // witness without annex
+ if (!witness || witness.length < 2) return;
+
+ return {
+ output: witness[witness.length - 2],
+ witness: witness.slice(0, -2),
+ redeemVersion: witness[witness.length - 1][0] & TAPLEAF_VERSION_MASK,
+ };
+ });
+ lazy.prop(o, 'pubkey', () => {
+ if (a.pubkey) return a.pubkey;
+ if (a.output) return a.output.slice(2);
+ if (a.address) return _address().data;
+ if (o.internalPubkey) {
+ const tweakedKey = tweakKey(o.internalPubkey, o.hash);
+ if (tweakedKey) return tweakedKey.x;
+ }
+ });
+ lazy.prop(o, 'internalPubkey', () => {
+ if (a.internalPubkey) return a.internalPubkey;
+ const witness = _witness();
+ if (witness && witness.length > 1)
+ return witness[witness.length - 1].slice(1, 33);
+ });
+ lazy.prop(o, 'signature', () => {
+ if (a.signature) return a.signature;
+ const witness = _witness(); // witness without annex
+ if (!witness || witness.length !== 1) return;
+ return witness[0];
+ });
+
+ lazy.prop(o, 'witness', () => {
+ if (a.witness) return a.witness;
+ const hashTree = _hashTree();
+ if (hashTree && a.redeem && a.redeem.output && a.internalPubkey) {
+ const leafHash = tapleafHash({
+ output: a.redeem.output,
+ version: o.redeemVersion,
+ });
+ const path = findScriptPath(hashTree, leafHash);
+ if (!path) return;
+ const outputKey = tweakKey(a.internalPubkey, hashTree.hash);
+ if (!outputKey) return;
+ const controlBock = tools.concat(
+ [
+ Uint8Array.from([o.redeemVersion! | outputKey.parity]),
+ a.internalPubkey,
+ ].concat(path),
+ );
+ return [a.redeem.output, controlBock];
+ }
+ if (a.signature) return [a.signature];
+ });
+
+ // extended validation
+ if (opts.validate) {
+ let pubkey: Uint8Array = Uint8Array.from([]);
+ if (a.address) {
+ if (network && network.bech32 !== _address().prefix)
+ throw new TypeError('Invalid prefix or Network mismatch');
+ if (_address().version !== TAPROOT_WITNESS_VERSION)
+ throw new TypeError('Invalid address version');
+ if (_address().data.length !== 32)
+ throw new TypeError('Invalid address data');
+ pubkey = _address().data;
+ }
+
+ if (a.pubkey) {
+ if (pubkey.length > 0 && tools.compare(pubkey, a.pubkey) !== 0)
+ throw new TypeError('Pubkey mismatch');
+ else pubkey = a.pubkey;
+ }
+
+ if (a.output) {
+ if (
+ a.output.length !== 34 ||
+ a.output[0] !== OPS.OP_1 ||
+ a.output[1] !== 0x20
+ )
+ throw new TypeError('Output is invalid');
+ if (pubkey.length > 0 && tools.compare(pubkey, a.output.slice(2)) !== 0)
+ throw new TypeError('Pubkey mismatch');
+ else pubkey = a.output.slice(2);
+ }
+
+ if (a.internalPubkey) {
+ const tweakedKey = tweakKey(a.internalPubkey, o.hash);
+ if (pubkey.length > 0 && tools.compare(pubkey, tweakedKey!.x) !== 0)
+ throw new TypeError('Pubkey mismatch');
+ else pubkey = tweakedKey!.x;
+ }
+
+ if (pubkey && pubkey.length) {
+ if (!getEccLib().isXOnlyPoint(pubkey))
+ throw new TypeError('Invalid pubkey for p2tr');
+ }
+
+ const hashTree = _hashTree();
+
+ if (a.hash && hashTree) {
+ if (tools.compare(a.hash, hashTree.hash) !== 0)
+ throw new TypeError('Hash mismatch');
+ }
+
+ if (a.redeem && a.redeem.output && hashTree) {
+ const leafHash = tapleafHash({
+ output: a.redeem.output,
+ version: o.redeemVersion,
+ });
+ if (!findScriptPath(hashTree, leafHash))
+ throw new TypeError('Redeem script not in tree');
+ }
+
+ const witness = _witness();
+
+ // compare the provided redeem data with the one computed from witness
+ if (a.redeem && o.redeem) {
+ if (a.redeem.redeemVersion) {
+ if (a.redeem.redeemVersion !== o.redeem.redeemVersion)
+ throw new TypeError('Redeem.redeemVersion and witness mismatch');
+ }
+
+ if (a.redeem.output) {
+ if (bscript.decompile(a.redeem.output)!.length === 0)
+ throw new TypeError('Redeem.output is invalid');
+
+ // output redeem is constructed from the witness
+ if (
+ o.redeem.output &&
+ tools.compare(a.redeem.output, o.redeem.output) !== 0
+ )
+ throw new TypeError('Redeem.output and witness mismatch');
+ }
+ if (a.redeem.witness) {
+ if (
+ o.redeem.witness &&
+ !stacksEqual(a.redeem.witness, o.redeem.witness)
+ )
+ throw new TypeError('Redeem.witness and witness mismatch');
+ }
+ }
+
+ if (witness && witness.length) {
+ if (witness.length === 1) {
+ // key spending
+ if (a.signature && tools.compare(a.signature, witness[0]) !== 0)
+ throw new TypeError('Signature mismatch');
+ } else {
+ // script path spending
+ const controlBlock = witness[witness.length - 1];
+ if (controlBlock.length < 33)
+ throw new TypeError(
+ `The control-block length is too small. Got ${controlBlock.length}, expected min 33.`,
+ );
+
+ if ((controlBlock.length - 33) % 32 !== 0)
+ throw new TypeError(
+ `The control-block length of ${controlBlock.length} is incorrect!`,
+ );
+
+ const m = (controlBlock.length - 33) / 32;
+ if (m > 128)
+ throw new TypeError(
+ `The script path is too long. Got ${m}, expected max 128.`,
+ );
+
+ const internalPubkey = controlBlock.slice(1, 33);
+ if (
+ a.internalPubkey &&
+ tools.compare(a.internalPubkey, internalPubkey) !== 0
+ )
+ throw new TypeError('Internal pubkey mismatch');
+
+ if (!getEccLib().isXOnlyPoint(internalPubkey))
+ throw new TypeError('Invalid internalPubkey for p2tr witness');
+
+ const leafVersion = controlBlock[0] & TAPLEAF_VERSION_MASK;
+ const script = witness[witness.length - 2];
+
+ const leafHash = tapleafHash({ output: script, version: leafVersion });
+ const hash = rootHashFromPath(controlBlock, leafHash);
+
+ const outputKey = tweakKey(internalPubkey, hash);
+ if (!outputKey)
+ // todo: needs test data
+ throw new TypeError('Invalid outputKey for p2tr witness');
+
+ if (pubkey.length && tools.compare(pubkey, outputKey.x) !== 0)
+ throw new TypeError('Pubkey mismatch for p2tr witness');
+
+ if (outputKey.parity !== (controlBlock[0] & 1))
+ throw new Error('Incorrect parity');
+ }
+ }
+ }
+
+ return Object.assign(o, a);
+}
diff --git a/ts_src/payments/p2wpkh.ts b/ts_src/payments/p2wpkh.ts
index a4497fece..326181ec4 100644
--- a/ts_src/payments/p2wpkh.ts
+++ b/ts_src/payments/p2wpkh.ts
@@ -1,33 +1,51 @@
-import * as bcrypto from '../crypto';
-import { bitcoin as BITCOIN_NETWORK } from '../networks';
-import * as bscript from '../script';
-import { isPoint, typeforce as typef } from '../types';
-import { Payment, PaymentOpts } from './index';
-import * as lazy from './lazy';
+import * as bcrypto from '../crypto.js';
+import { bitcoin as BITCOIN_NETWORK } from '../networks.js';
+import * as bscript from '../script.js';
+import { BufferSchema, isPoint, NBufferSchemaFactory } from '../types.js';
+import { Payment, PaymentOpts } from './index.js';
+import * as lazy from './lazy.js';
import { bech32 } from 'bech32';
+import * as tools from 'uint8array-tools';
+import * as v from 'valibot';
+
const OPS = bscript.OPS;
-const EMPTY_BUFFER = Buffer.alloc(0);
+const EMPTY_BUFFER = new Uint8Array(0);
// witness: {signature} {pubKey}
// input: <>
// output: OP_0 {pubKeyHash}
+/**
+ * Creates a pay-to-witness-public-key-hash (p2wpkh) payment object.
+ *
+ * @param a - The payment object containing the necessary data.
+ * @param opts - Optional payment options.
+ * @returns The p2wpkh payment object.
+ * @throws {TypeError} If the required data is missing or invalid.
+ */
export function p2wpkh(a: Payment, opts?: PaymentOpts): Payment {
if (!a.address && !a.hash && !a.output && !a.pubkey && !a.witness)
throw new TypeError('Not enough data');
opts = Object.assign({ validate: true }, opts || {});
- typef(
- {
- address: typef.maybe(typef.String),
- hash: typef.maybe(typef.BufferN(20)),
- input: typef.maybe(typef.BufferN(0)),
- network: typef.maybe(typef.Object),
- output: typef.maybe(typef.BufferN(22)),
- pubkey: typef.maybe(isPoint),
- signature: typef.maybe(bscript.isCanonicalScriptSignature),
- witness: typef.maybe(typef.arrayOf(typef.Buffer)),
- },
+ v.parse(
+ v.partial(
+ v.object({
+ address: v.string(),
+ hash: NBufferSchemaFactory(20),
+ input: NBufferSchemaFactory(0),
+ network: v.object({}),
+ output: NBufferSchemaFactory(22),
+ pubkey: v.custom(
+ isPoint as (input: unknown) => boolean,
+ 'Not a valid pubkey',
+ ),
+ signature: v.custom(
+ bscript.isCanonicalScriptSignature as (input: unknown) => boolean,
+ ),
+ witness: v.array(BufferSchema),
+ }),
+ ),
a,
);
@@ -38,7 +56,7 @@ export function p2wpkh(a: Payment, opts?: PaymentOpts): Payment {
return {
version,
prefix: result.prefix,
- data: Buffer.from(data),
+ data: Uint8Array.from(data),
};
});
@@ -82,7 +100,7 @@ export function p2wpkh(a: Payment, opts?: PaymentOpts): Payment {
// extended validation
if (opts.validate) {
- let hash: Buffer = Buffer.from([]);
+ let hash: Uint8Array = Uint8Array.from([]);
if (a.address) {
if (network && network.bech32 !== _address().prefix)
throw new TypeError('Invalid prefix or Network mismatch');
@@ -94,7 +112,7 @@ export function p2wpkh(a: Payment, opts?: PaymentOpts): Payment {
}
if (a.hash) {
- if (hash.length > 0 && !hash.equals(a.hash))
+ if (hash.length > 0 && tools.compare(hash, a.hash) !== 0)
throw new TypeError('Hash mismatch');
else hash = a.hash;
}
@@ -106,14 +124,14 @@ export function p2wpkh(a: Payment, opts?: PaymentOpts): Payment {
a.output[1] !== 0x14
)
throw new TypeError('Output is invalid');
- if (hash.length > 0 && !hash.equals(a.output.slice(2)))
+ if (hash.length > 0 && tools.compare(hash, a.output.slice(2)) !== 0)
throw new TypeError('Hash mismatch');
else hash = a.output.slice(2);
}
if (a.pubkey) {
const pkh = bcrypto.hash160(a.pubkey);
- if (hash.length > 0 && !hash.equals(pkh))
+ if (hash.length > 0 && tools.compare(hash, pkh) !== 0)
throw new TypeError('Hash mismatch');
else hash = pkh;
if (!isPoint(a.pubkey) || a.pubkey.length !== 33)
@@ -127,13 +145,14 @@ export function p2wpkh(a: Payment, opts?: PaymentOpts): Payment {
if (!isPoint(a.witness[1]) || a.witness[1].length !== 33)
throw new TypeError('Witness has invalid pubkey');
- if (a.signature && !a.signature.equals(a.witness[0]))
+ if (a.signature && tools.compare(a.signature, a.witness[0]) !== 0)
throw new TypeError('Signature mismatch');
- if (a.pubkey && !a.pubkey.equals(a.witness[1]))
+ // if (a.pubkey && !a.pubkey.equals(a.witness[1]))
+ if (a.pubkey && tools.compare(a.pubkey, a.witness[1]) !== 0)
throw new TypeError('Pubkey mismatch');
const pkh = bcrypto.hash160(a.witness[1]);
- if (hash.length > 0 && !hash.equals(pkh))
+ if (hash.length > 0 && tools.compare(hash, pkh) !== 0)
throw new TypeError('Hash mismatch');
}
}
diff --git a/ts_src/payments/p2wsh.ts b/ts_src/payments/p2wsh.ts
index 00860e0b9..ae8c8b12e 100644
--- a/ts_src/payments/p2wsh.ts
+++ b/ts_src/payments/p2wsh.ts
@@ -1,25 +1,27 @@
-import * as bcrypto from '../crypto';
-import { bitcoin as BITCOIN_NETWORK } from '../networks';
-import * as bscript from '../script';
-import { isPoint, typeforce as typef } from '../types';
-import { Payment, PaymentOpts, StackElement, StackFunction } from './index';
-import * as lazy from './lazy';
+import { sha256 } from '@noble/hashes/sha256';
+import { bitcoin as BITCOIN_NETWORK } from '../networks.js';
+import * as bscript from '../script.js';
+import {
+ Buffer256bitSchema,
+ BufferSchema,
+ isPoint,
+ NBufferSchemaFactory,
+ stacksEqual,
+ NullablePartial,
+} from '../types.js';
+import { Payment, PaymentOpts, StackElement, StackFunction } from './index.js';
+import * as lazy from './lazy.js';
import { bech32 } from 'bech32';
-const OPS = bscript.OPS;
+import * as tools from 'uint8array-tools';
+import * as v from 'valibot';
-const EMPTY_BUFFER = Buffer.alloc(0);
+const OPS = bscript.OPS;
-function stacksEqual(a: Buffer[], b: Buffer[]): boolean {
- if (a.length !== b.length) return false;
-
- return a.every((x, i) => {
- return x.equals(b[i]);
- });
-}
+const EMPTY_BUFFER = new Uint8Array(0);
function chunkHasUncompressedPubkey(chunk: StackElement): boolean {
if (
- Buffer.isBuffer(chunk) &&
+ chunk instanceof Uint8Array &&
chunk.length === 65 &&
chunk[0] === 0x04 &&
isPoint(chunk)
@@ -33,28 +35,36 @@ function chunkHasUncompressedPubkey(chunk: StackElement): boolean {
// input: <>
// witness: [redeemScriptSig ...] {redeemScript}
// output: OP_0 {sha256(redeemScript)}
+/**
+ * Creates a Pay-to-Witness-Script-Hash (P2WSH) payment object.
+ *
+ * @param a - The payment object containing the necessary data.
+ * @param opts - Optional payment options.
+ * @returns The P2WSH payment object.
+ * @throws {TypeError} If the required data is missing or invalid.
+ */
export function p2wsh(a: Payment, opts?: PaymentOpts): Payment {
if (!a.address && !a.hash && !a.output && !a.redeem && !a.witness)
throw new TypeError('Not enough data');
opts = Object.assign({ validate: true }, opts || {});
- typef(
- {
- network: typef.maybe(typef.Object),
+ v.parse(
+ NullablePartial({
+ network: v.object({}),
- address: typef.maybe(typef.String),
- hash: typef.maybe(typef.BufferN(32)),
- output: typef.maybe(typef.BufferN(34)),
+ address: v.string(),
+ hash: Buffer256bitSchema,
+ output: NBufferSchemaFactory(34),
- redeem: typef.maybe({
- input: typef.maybe(typef.Buffer),
- network: typef.maybe(typef.Object),
- output: typef.maybe(typef.Buffer),
- witness: typef.maybe(typef.arrayOf(typef.Buffer)),
+ redeem: NullablePartial({
+ input: BufferSchema,
+ network: v.object({}),
+ output: BufferSchema,
+ witness: v.array(BufferSchema),
}),
- input: typef.maybe(typef.BufferN(0)),
- witness: typef.maybe(typef.arrayOf(typef.Buffer)),
- },
+ input: NBufferSchemaFactory(0),
+ witness: v.array(BufferSchema),
+ }),
a,
);
@@ -65,7 +75,7 @@ export function p2wsh(a: Payment, opts?: PaymentOpts): Payment {
return {
version,
prefix: result.prefix,
- data: Buffer.from(data),
+ data: Uint8Array.from(data),
};
});
const _rchunks = lazy.value(() => {
@@ -88,7 +98,7 @@ export function p2wsh(a: Payment, opts?: PaymentOpts): Payment {
lazy.prop(o, 'hash', () => {
if (a.output) return a.output.slice(2);
if (a.address) return _address().data;
- if (o.redeem && o.redeem.output) return bcrypto.sha256(o.redeem.output);
+ if (o.redeem && o.redeem.output) return sha256(o.redeem.output);
});
lazy.prop(o, 'output', () => {
if (!o.hash) return;
@@ -120,13 +130,13 @@ export function p2wsh(a: Payment, opts?: PaymentOpts): Payment {
// assign, and blank the existing input
o.redeem = Object.assign({ witness: stack }, a.redeem);
o.redeem.input = EMPTY_BUFFER;
- return ([] as Buffer[]).concat(stack, a.redeem.output);
+ return ([] as Uint8Array[]).concat(stack, a.redeem.output);
}
if (!a.redeem) return;
if (!a.redeem.output) return;
if (!a.redeem.witness) return;
- return ([] as Buffer[]).concat(a.redeem.witness, a.redeem.output);
+ return ([] as Uint8Array[]).concat(a.redeem.witness, a.redeem.output);
});
lazy.prop(o, 'name', () => {
const nameParts = ['p2wsh'];
@@ -137,7 +147,7 @@ export function p2wsh(a: Payment, opts?: PaymentOpts): Payment {
// extended validation
if (opts.validate) {
- let hash: Buffer = Buffer.from([]);
+ let hash = Uint8Array.from([]);
if (a.address) {
if (_address().prefix !== network.bech32)
throw new TypeError('Invalid prefix or Network mismatch');
@@ -149,7 +159,7 @@ export function p2wsh(a: Payment, opts?: PaymentOpts): Payment {
}
if (a.hash) {
- if (hash.length > 0 && !hash.equals(a.hash))
+ if (hash.length > 0 && tools.compare(hash, a.hash) !== 0)
throw new TypeError('Hash mismatch');
else hash = a.hash;
}
@@ -162,7 +172,7 @@ export function p2wsh(a: Payment, opts?: PaymentOpts): Payment {
)
throw new TypeError('Output is invalid');
const hash2 = a.output.slice(2);
- if (hash.length > 0 && !hash.equals(hash2))
+ if (hash.length > 0 && tools.compare(hash, hash2) !== 0)
throw new TypeError('Hash mismatch');
else hash = hash2;
}
@@ -180,14 +190,23 @@ export function p2wsh(a: Payment, opts?: PaymentOpts): Payment {
)
throw new TypeError('Ambiguous witness source');
- // is the redeem output non-empty?
+ // is the redeem output non-empty/valid?
if (a.redeem.output) {
- if (bscript.decompile(a.redeem.output)!.length === 0)
+ const decompile = bscript.decompile(a.redeem.output);
+ if (!decompile || decompile.length < 1)
throw new TypeError('Redeem.output is invalid');
+ if (a.redeem.output.byteLength > 3600)
+ throw new TypeError(
+ 'Redeem.output unspendable if larger than 3600 bytes',
+ );
+ if (bscript.countNonPushOnlyOPs(decompile) > 201)
+ throw new TypeError(
+ 'Redeem.output unspendable with more than 201 non-push ops',
+ );
// match hash against other sources
- const hash2 = bcrypto.sha256(a.redeem.output);
- if (hash.length > 0 && !hash.equals(hash2))
+ const hash2 = sha256(a.redeem.output);
+ if (hash.length > 0 && tools.compare(hash, hash2) !== 0)
throw new TypeError('Hash mismatch');
else hash = hash2;
}
@@ -215,7 +234,11 @@ export function p2wsh(a: Payment, opts?: PaymentOpts): Payment {
if (a.witness && a.witness.length > 0) {
const wScript = a.witness[a.witness.length - 1];
- if (a.redeem && a.redeem.output && !a.redeem.output.equals(wScript))
+ if (
+ a.redeem &&
+ a.redeem.output &&
+ tools.compare(a.redeem.output, wScript) !== 0
+ )
throw new TypeError('Witness and redeem.output mismatch');
if (
a.witness.some(chunkHasUncompressedPubkey) ||
diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts
index b9af10fcf..f072f1d48 100644
--- a/ts_src/psbt.ts
+++ b/ts_src/psbt.ts
@@ -1,5 +1,5 @@
import { Psbt as PsbtBase } from 'bip174';
-import * as varuint from 'bip174/src/lib/converter/varint';
+import * as varuint from 'varuint-bitcoin';
import {
Bip32Derivation,
KeyValue,
@@ -11,29 +11,56 @@ import {
PsbtOutputUpdate,
Transaction as ITransaction,
TransactionFromBuffer,
-} from 'bip174/src/lib/interfaces';
-import { checkForInput, checkForOutput } from 'bip174/src/lib/utils';
-import { fromOutputScript, toOutputScript } from './address';
-import { cloneBuffer, reverseBuffer } from './bufferutils';
-import { hash160 } from './crypto';
-import { bitcoin as btcNetwork, Network } from './networks';
-import * as payments from './payments';
-import * as bscript from './script';
-import { Output, Transaction } from './transaction';
+ TapKeySig,
+ TapScriptSig,
+ TapLeafScript,
+} from 'bip174';
+import { checkForInput, checkForOutput } from 'bip174';
+import { fromOutputScript, toOutputScript } from './address.js';
+import { cloneBuffer, reverseBuffer } from './bufferutils.js';
+import { bitcoin as btcNetwork, Network } from './networks.js';
+import * as payments from './payments/index.js';
+import { tapleafHash } from './payments/bip341.js';
+import * as bscript from './script.js';
+import { Output, Transaction } from './transaction.js';
+import {
+ toXOnly,
+ tapScriptFinalizer,
+ serializeTaprootSignature,
+ isTaprootInput,
+ checkTaprootInputFields,
+ checkTaprootOutputFields,
+ checkTaprootInputForSigs,
+} from './psbt/bip371.js';
+import {
+ witnessStackToScriptWitness,
+ checkInputForSig,
+ pubkeyInScript,
+ isP2MS,
+ isP2PK,
+ isP2PKH,
+ isP2WPKH,
+ isP2WSHScript,
+ isP2SHScript,
+ isP2TR,
+} from './psbt/psbtutils.js';
+import * as tools from 'uint8array-tools';
+
+export { toXOnly };
export interface TransactionInput {
- hash: string | Buffer;
+ hash: string | Uint8Array;
index: number;
sequence?: number;
}
export interface PsbtTxInput extends TransactionInput {
- hash: Buffer;
+ hash: Uint8Array;
}
export interface TransactionOutput {
- script: Buffer;
- value: number;
+ script: Uint8Array;
+ value: bigint;
}
export interface PsbtTxOutput extends TransactionOutput {
@@ -42,9 +69,9 @@ export interface PsbtTxOutput extends TransactionOutput {
// msghash is 32 byte hash of preimage, signature is 64 byte compact signature (r,s 32 bytes each)
export type ValidateSigFunction = (
- pubkey: Buffer,
- msghash: Buffer,
- signature: Buffer,
+ pubkey: Uint8Array,
+ msghash: Uint8Array,
+ signature: Uint8Array,
) => boolean;
/**
@@ -69,6 +96,7 @@ const DEFAULT_OPTS: PsbtOpts = {
* There are 6 roles that this class fulfills. (Explained in BIP174)
*
* Creator: This can be done with `new Psbt()`
+ *
* Updater: This can be done with `psbt.addInput(input)`, `psbt.addInputs(inputs)`,
* `psbt.addOutput(output)`, `psbt.addOutputs(outputs)` when you are looking to
* add new inputs and outputs to the PSBT, and `psbt.updateGlobal(itemObject)`,
@@ -79,35 +107,39 @@ const DEFAULT_OPTS: PsbtOpts = {
* data for updateOutput.
* For a list of what attributes should be what types. Check the bip174 library.
* Also, check the integration tests for some examples of usage.
+ *
* Signer: There are a few methods. signAllInputs and signAllInputsAsync, which will search all input
* information for your pubkey or pubkeyhash, and only sign inputs where it finds
* your info. Or you can explicitly sign a specific input with signInput and
* signInputAsync. For the async methods you can create a SignerAsync object
* and use something like a hardware wallet to sign with. (You must implement this)
+ *
* Combiner: psbts can be combined easily with `psbt.combine(psbt2, psbt3, psbt4 ...)`
* the psbt calling combine will always have precedence when a conflict occurs.
* Combine checks if the internal bitcoin transaction is the same, so be sure that
* all sequences, version, locktime, etc. are the same before combining.
+ *
* Input Finalizer: This role is fairly important. Not only does it need to construct
* the input scriptSigs and witnesses, but it SHOULD verify the signatures etc.
* Before running `psbt.finalizeAllInputs()` please run `psbt.validateSignaturesOfAllInputs()`
* Running any finalize method will delete any data in the input(s) that are no longer
* needed due to the finalized scripts containing the information.
+ *
* Transaction Extractor: This role will perform some checks before returning a
* Transaction object. Such as fee rate not being larger than maximumFeeRate etc.
*/
export class Psbt {
static fromBase64(data: string, opts: PsbtOptsOptional = {}): Psbt {
- const buffer = Buffer.from(data, 'base64');
+ const buffer = tools.fromBase64(data);
return this.fromBuffer(buffer, opts);
}
static fromHex(data: string, opts: PsbtOptsOptional = {}): Psbt {
- const buffer = Buffer.from(data, 'hex');
+ const buffer = tools.fromHex(data);
return this.fromBuffer(buffer, opts);
}
- static fromBuffer(buffer: Buffer, opts: PsbtOptsOptional = {}): Psbt {
+ static fromBuffer(buffer: Uint8Array, opts: PsbtOptsOptional = {}): Psbt {
const psbtBase = PsbtBase.fromBuffer(buffer, transactionFromBuffer);
const psbt = new Psbt(opts, psbtBase);
checkTxForDupeIns(psbt.__CACHE.__TX, psbt.__CACHE);
@@ -128,7 +160,7 @@ export class Psbt {
__NON_WITNESS_UTXO_BUF_CACHE: [],
__TX_IN_CACHE: {},
__TX: (this.data.globalMap.unsignedTx as PsbtTransaction).tx,
- // Psbt's predecesor (TransactionBuilder - now removed) behavior
+ // Psbt's predecessor (TransactionBuilder - now removed) behavior
// was to not confirm input values before signing.
// Even though we highly encourage people to get
// the full parent transaction to verify values, the ability to
@@ -261,6 +293,7 @@ export class Psbt {
`Requires single object with at least [hash] and [index]`,
);
}
+ checkTaprootInputFields(inputData, inputData, 'addInput');
checkInputsForPartialSig(this.data.inputs, 'addInput');
if (inputData.witnessScript) checkInvalidP2WSH(inputData.witnessScript);
const c = this.__CACHE;
@@ -302,8 +335,10 @@ export class Psbt {
if (typeof address === 'string') {
const { network } = this.opts;
const script = toOutputScript(address, network);
- outputData = Object.assign(outputData, { script });
+ outputData = Object.assign({}, outputData, { script });
}
+ checkTaprootOutputFields(outputData, outputData, 'addOutput');
+
const c = this.__CACHE;
this.data.addOutput(outputData);
c.__FEE = undefined;
@@ -330,11 +365,16 @@ export class Psbt {
'fee rate',
this.data.inputs,
this.__CACHE,
- )!;
+ )! as number;
}
- getFee(): number {
- return getTxCacheValue('__FEE', 'fee', this.data.inputs, this.__CACHE)!;
+ getFee(): bigint {
+ return getTxCacheValue(
+ '__FEE',
+ 'fee',
+ this.data.inputs,
+ this.__CACHE,
+ )! as bigint;
}
finalizeAllInputs(): this {
@@ -345,9 +385,44 @@ export class Psbt {
finalizeInput(
inputIndex: number,
- finalScriptsFunc: FinalScriptsFunc = getFinalScripts,
+ finalScriptsFunc?: FinalScriptsFunc | FinalTaprootScriptsFunc,
): this {
const input = checkForInput(this.data.inputs, inputIndex);
+ if (isTaprootInput(input))
+ return this._finalizeTaprootInput(
+ inputIndex,
+ input,
+ undefined,
+ finalScriptsFunc as FinalTaprootScriptsFunc,
+ );
+ return this._finalizeInput(
+ inputIndex,
+ input,
+ finalScriptsFunc as FinalScriptsFunc,
+ );
+ }
+
+ finalizeTaprootInput(
+ inputIndex: number,
+ tapLeafHashToFinalize?: Uint8Array,
+ finalScriptsFunc: FinalTaprootScriptsFunc = tapScriptFinalizer,
+ ): this {
+ const input = checkForInput(this.data.inputs, inputIndex);
+ if (isTaprootInput(input))
+ return this._finalizeTaprootInput(
+ inputIndex,
+ input,
+ tapLeafHashToFinalize,
+ finalScriptsFunc,
+ );
+ throw new Error(`Cannot finalize input #${inputIndex}. Not Taproot.`);
+ }
+
+ private _finalizeInput(
+ inputIndex: number,
+ input: PsbtInput,
+ finalScriptsFunc: FinalScriptsFunc = getFinalScripts,
+ ): this {
const { script, isP2SH, isP2WSH, isSegwit } = getScriptFromInput(
inputIndex,
input,
@@ -376,6 +451,39 @@ export class Psbt {
return this;
}
+ private _finalizeTaprootInput(
+ inputIndex: number,
+ input: PsbtInput,
+ tapLeafHashToFinalize?: Uint8Array,
+ finalScriptsFunc = tapScriptFinalizer,
+ ): this {
+ if (!input.witnessUtxo)
+ throw new Error(
+ `Cannot finalize input #${inputIndex}. Missing withness utxo.`,
+ );
+
+ // Check key spend first. Increased privacy and reduced block space.
+ if (input.tapKeySig) {
+ const payment = payments.p2tr({
+ output: input.witnessUtxo.script,
+ signature: input.tapKeySig,
+ });
+ const finalScriptWitness = witnessStackToScriptWitness(payment.witness!);
+ this.data.updateInput(inputIndex, { finalScriptWitness });
+ } else {
+ const { finalScriptWitness } = finalScriptsFunc(
+ inputIndex,
+ input,
+ tapLeafHashToFinalize,
+ );
+ this.data.updateInput(inputIndex, { finalScriptWitness });
+ }
+
+ this.data.clearFinalizedInput(inputIndex);
+
+ return this;
+ }
+
getInputType(inputIndex: number): AllScriptType {
const input = checkForInput(this.data.inputs, inputIndex);
const script = getScriptFromUtxo(inputIndex, input, this.__CACHE);
@@ -392,7 +500,7 @@ export class Psbt {
return (type + mainType) as AllScriptType;
}
- inputHasPubkey(inputIndex: number, pubkey: Buffer): boolean {
+ inputHasPubkey(inputIndex: number, pubkey: Uint8Array): boolean {
const input = checkForInput(this.data.inputs, inputIndex);
return pubkeyInInput(pubkey, input, inputIndex, this.__CACHE);
}
@@ -405,7 +513,7 @@ export class Psbt {
);
}
- outputHasPubkey(outputIndex: number, pubkey: Buffer): boolean {
+ outputHasPubkey(outputIndex: number, pubkey: Uint8Array): boolean {
const output = checkForOutput(this.data.outputs, outputIndex);
return pubkeyInOutput(pubkey, output, outputIndex, this.__CACHE);
}
@@ -429,21 +537,37 @@ export class Psbt {
validateSignaturesOfInput(
inputIndex: number,
validator: ValidateSigFunction,
- pubkey?: Buffer,
+ pubkey?: Uint8Array,
+ ): boolean {
+ const input = this.data.inputs[inputIndex];
+ if (isTaprootInput(input))
+ return this.validateSignaturesOfTaprootInput(
+ inputIndex,
+ validator,
+ pubkey,
+ );
+
+ return this._validateSignaturesOfInput(inputIndex, validator, pubkey);
+ }
+
+ private _validateSignaturesOfInput(
+ inputIndex: number,
+ validator: ValidateSigFunction,
+ pubkey?: Uint8Array,
): boolean {
const input = this.data.inputs[inputIndex];
- const partialSig = (input || {}).partialSig;
+ const partialSig = (input || {}).partialSig as PartialSig[];
if (!input || !partialSig || partialSig.length < 1)
throw new Error('No signatures to validate');
if (typeof validator !== 'function')
throw new Error('Need validator function to validate signatures');
const mySigs = pubkey
- ? partialSig.filter(sig => sig.pubkey.equals(pubkey))
+ ? partialSig.filter(sig => tools.compare(sig.pubkey, pubkey) === 0)
: partialSig;
if (mySigs.length < 1) throw new Error('No signatures for this pubkey');
const results: boolean[] = [];
- let hashCache: Buffer;
- let scriptCache: Buffer;
+ let hashCache: Uint8Array;
+ let scriptCache: Uint8Array;
let sighashCache: number;
for (const pSig of mySigs) {
const sig = bscript.signature.decode(pSig.signature);
@@ -465,6 +589,69 @@ export class Psbt {
return results.every(res => res === true);
}
+ private validateSignaturesOfTaprootInput(
+ inputIndex: number,
+ validator: ValidateSigFunction,
+ pubkey?: Uint8Array,
+ ): boolean {
+ const input = this.data.inputs[inputIndex];
+ const tapKeySig = (input || {}).tapKeySig;
+ const tapScriptSig = (input || {}).tapScriptSig;
+ if (!input && !tapKeySig && !(tapScriptSig && !tapScriptSig.length))
+ throw new Error('No signatures to validate');
+ if (typeof validator !== 'function')
+ throw new Error('Need validator function to validate signatures');
+
+ pubkey = pubkey && toXOnly(pubkey);
+ const allHashses = pubkey
+ ? getTaprootHashesForSigValidation(
+ inputIndex,
+ input,
+ this.data.inputs,
+ pubkey,
+ this.__CACHE,
+ )
+ : getAllTaprootHashesForSigValidation(
+ inputIndex,
+ input,
+ this.data.inputs,
+ this.__CACHE,
+ );
+
+ if (!allHashses.length) throw new Error('No signatures for this pubkey');
+
+ const tapKeyHash = allHashses.find(h => !h.leafHash);
+ let validationResultCount = 0;
+ if (tapKeySig && tapKeyHash) {
+ const isValidTapkeySig = validator(
+ tapKeyHash.pubkey,
+ tapKeyHash.hash,
+ trimTaprootSig(tapKeySig),
+ );
+ if (!isValidTapkeySig) return false;
+ validationResultCount++;
+ }
+
+ if (tapScriptSig) {
+ for (const tapSig of tapScriptSig) {
+ const tapSigHash = allHashses.find(
+ h => tools.compare(h.pubkey, tapSig.pubkey) === 0,
+ );
+ if (tapSigHash) {
+ const isValidTapScriptSig = validator(
+ tapSig.pubkey,
+ tapSigHash.hash,
+ trimTaprootSig(tapSig.signature),
+ );
+ if (!isValidTapScriptSig) return false;
+ validationResultCount++;
+ }
+ }
+ }
+
+ return validationResultCount > 0;
+ }
+
signAllInputsHD(
hdKeyPair: HDSigner,
sighashTypes: number[] = [Transaction.SIGHASH_ALL],
@@ -492,34 +679,32 @@ export class Psbt {
hdKeyPair: HDSigner | HDSignerAsync,
sighashTypes: number[] = [Transaction.SIGHASH_ALL],
): Promise {
- return new Promise(
- (resolve, reject): any => {
- if (!hdKeyPair || !hdKeyPair.publicKey || !hdKeyPair.fingerprint) {
- return reject(new Error('Need HDSigner to sign input'));
- }
+ return new Promise((resolve, reject): any => {
+ if (!hdKeyPair || !hdKeyPair.publicKey || !hdKeyPair.fingerprint) {
+ return reject(new Error('Need HDSigner to sign input'));
+ }
- const results: boolean[] = [];
- const promises: Array> = [];
- for (const i of range(this.data.inputs.length)) {
- promises.push(
- this.signInputHDAsync(i, hdKeyPair, sighashTypes).then(
- () => {
- results.push(true);
- },
- () => {
- results.push(false);
- },
- ),
- );
+ const results: boolean[] = [];
+ const promises: Array> = [];
+ for (const i of range(this.data.inputs.length)) {
+ promises.push(
+ this.signInputHDAsync(i, hdKeyPair, sighashTypes).then(
+ () => {
+ results.push(true);
+ },
+ () => {
+ results.push(false);
+ },
+ ),
+ );
+ }
+ return Promise.all(promises).then(() => {
+ if (results.every(v => v === false)) {
+ return reject(new Error('No inputs were signed'));
}
- return Promise.all(promises).then(() => {
- if (results.every(v => v === false)) {
- return reject(new Error('No inputs were signed'));
- }
- resolve();
- });
- },
- );
+ resolve();
+ });
+ });
}
signInputHD(
@@ -544,32 +729,23 @@ export class Psbt {
hdKeyPair: HDSigner | HDSignerAsync,
sighashTypes: number[] = [Transaction.SIGHASH_ALL],
): Promise {
- return new Promise(
- (resolve, reject): any => {
- if (!hdKeyPair || !hdKeyPair.publicKey || !hdKeyPair.fingerprint) {
- return reject(new Error('Need HDSigner to sign input'));
- }
- const signers = getSignersFromHD(
- inputIndex,
- this.data.inputs,
- hdKeyPair,
- );
- const promises = signers.map(signer =>
- this.signInputAsync(inputIndex, signer, sighashTypes),
- );
- return Promise.all(promises)
- .then(() => {
- resolve();
- })
- .catch(reject);
- },
- );
+ return new Promise((resolve, reject): any => {
+ if (!hdKeyPair || !hdKeyPair.publicKey || !hdKeyPair.fingerprint) {
+ return reject(new Error('Need HDSigner to sign input'));
+ }
+ const signers = getSignersFromHD(inputIndex, this.data.inputs, hdKeyPair);
+ const promises = signers.map(signer =>
+ this.signInputAsync(inputIndex, signer, sighashTypes),
+ );
+ return Promise.all(promises)
+ .then(() => {
+ resolve();
+ })
+ .catch(reject);
+ });
}
- signAllInputs(
- keyPair: Signer,
- sighashTypes: number[] = [Transaction.SIGHASH_ALL],
- ): this {
+ signAllInputs(keyPair: Signer, sighashTypes?: number[]): this {
if (!keyPair || !keyPair.publicKey)
throw new Error('Need Signer to sign input');
@@ -593,47 +769,86 @@ export class Psbt {
signAllInputsAsync(
keyPair: Signer | SignerAsync,
- sighashTypes: number[] = [Transaction.SIGHASH_ALL],
+ sighashTypes?: number[],
): Promise {
- return new Promise(
- (resolve, reject): any => {
- if (!keyPair || !keyPair.publicKey)
- return reject(new Error('Need Signer to sign input'));
-
- // TODO: Add a pubkey/pubkeyhash cache to each input
- // as input information is added, then eventually
- // optimize this method.
- const results: boolean[] = [];
- const promises: Array> = [];
- for (const [i] of this.data.inputs.entries()) {
- promises.push(
- this.signInputAsync(i, keyPair, sighashTypes).then(
- () => {
- results.push(true);
- },
- () => {
- results.push(false);
- },
- ),
- );
+ return new Promise((resolve, reject): any => {
+ if (!keyPair || !keyPair.publicKey)
+ return reject(new Error('Need Signer to sign input'));
+
+ // TODO: Add a pubkey/pubkeyhash cache to each input
+ // as input information is added, then eventually
+ // optimize this method.
+ const results: boolean[] = [];
+ const promises: Array> = [];
+ for (const [i] of this.data.inputs.entries()) {
+ promises.push(
+ this.signInputAsync(i, keyPair, sighashTypes).then(
+ () => {
+ results.push(true);
+ },
+ () => {
+ results.push(false);
+ },
+ ),
+ );
+ }
+ return Promise.all(promises).then(() => {
+ if (results.every(v => v === false)) {
+ return reject(new Error('No inputs were signed'));
}
- return Promise.all(promises).then(() => {
- if (results.every(v => v === false)) {
- return reject(new Error('No inputs were signed'));
- }
- resolve();
- });
- },
- );
+ resolve();
+ });
+ });
}
signInput(
inputIndex: number,
keyPair: Signer,
- sighashTypes: number[] = [Transaction.SIGHASH_ALL],
+ sighashTypes?: number[],
+ ): this {
+ if (!keyPair || !keyPair.publicKey)
+ throw new Error('Need Signer to sign input');
+
+ const input = checkForInput(this.data.inputs, inputIndex);
+
+ if (isTaprootInput(input)) {
+ return this._signTaprootInput(
+ inputIndex,
+ input,
+ keyPair,
+ undefined,
+ sighashTypes,
+ );
+ }
+ return this._signInput(inputIndex, keyPair, sighashTypes);
+ }
+
+ signTaprootInput(
+ inputIndex: number,
+ keyPair: Signer,
+ tapLeafHashToSign?: Uint8Array,
+ sighashTypes?: number[],
): this {
if (!keyPair || !keyPair.publicKey)
throw new Error('Need Signer to sign input');
+ const input = checkForInput(this.data.inputs, inputIndex);
+
+ if (isTaprootInput(input))
+ return this._signTaprootInput(
+ inputIndex,
+ input,
+ keyPair,
+ tapLeafHashToSign,
+ sighashTypes,
+ );
+ throw new Error(`Input #${inputIndex} is not of type Taproot.`);
+ }
+
+ private _signInput(
+ inputIndex: number,
+ keyPair: Signer,
+ sighashTypes: number[] = [Transaction.SIGHASH_ALL],
+ ): this {
const { hash, sighashType } = getHashAndSighashType(
this.data.inputs,
inputIndex,
@@ -653,36 +868,215 @@ export class Psbt {
return this;
}
+ private _signTaprootInput(
+ inputIndex: number,
+ input: PsbtInput,
+ keyPair: Signer,
+ tapLeafHashToSign?: Uint8Array,
+ allowedSighashTypes: number[] = [Transaction.SIGHASH_DEFAULT],
+ ): this {
+ const hashesForSig = this.checkTaprootHashesForSig(
+ inputIndex,
+ input,
+ keyPair,
+ tapLeafHashToSign,
+ allowedSighashTypes,
+ );
+
+ const tapKeySig: TapKeySig = hashesForSig
+ .filter(h => !h.leafHash)
+ .map(h =>
+ serializeTaprootSignature(
+ keyPair.signSchnorr!(h.hash),
+ input.sighashType,
+ ),
+ )[0];
+
+ const tapScriptSig: TapScriptSig[] = hashesForSig
+ .filter(h => !!h.leafHash)
+ .map(
+ h =>
+ ({
+ pubkey: toXOnly(keyPair.publicKey),
+ signature: serializeTaprootSignature(
+ keyPair.signSchnorr!(h.hash),
+ input.sighashType,
+ ),
+ leafHash: h.leafHash,
+ }) as TapScriptSig,
+ );
+
+ if (tapKeySig) {
+ this.data.updateInput(inputIndex, { tapKeySig });
+ }
+
+ if (tapScriptSig.length) {
+ this.data.updateInput(inputIndex, { tapScriptSig });
+ }
+
+ return this;
+ }
+
signInputAsync(
inputIndex: number,
keyPair: Signer | SignerAsync,
- sighashTypes: number[] = [Transaction.SIGHASH_ALL],
+ sighashTypes?: number[],
): Promise {
return Promise.resolve().then(() => {
if (!keyPair || !keyPair.publicKey)
throw new Error('Need Signer to sign input');
- const { hash, sighashType } = getHashAndSighashType(
- this.data.inputs,
- inputIndex,
- keyPair.publicKey,
- this.__CACHE,
- sighashTypes,
- );
- return Promise.resolve(keyPair.sign(hash)).then(signature => {
- const partialSig = [
- {
- pubkey: keyPair.publicKey,
- signature: bscript.signature.encode(signature, sighashType),
- },
- ];
+ const input = checkForInput(this.data.inputs, inputIndex);
+ if (isTaprootInput(input))
+ return this._signTaprootInputAsync(
+ inputIndex,
+ input,
+ keyPair,
+ undefined,
+ sighashTypes,
+ );
+
+ return this._signInputAsync(inputIndex, keyPair, sighashTypes);
+ });
+ }
+
+ signTaprootInputAsync(
+ inputIndex: number,
+ keyPair: Signer | SignerAsync,
+ tapLeafHash?: Uint8Array,
+ sighashTypes?: number[],
+ ): Promise {
+ return Promise.resolve().then(() => {
+ if (!keyPair || !keyPair.publicKey)
+ throw new Error('Need Signer to sign input');
+
+ const input = checkForInput(this.data.inputs, inputIndex);
+ if (isTaprootInput(input))
+ return this._signTaprootInputAsync(
+ inputIndex,
+ input,
+ keyPair,
+ tapLeafHash,
+ sighashTypes,
+ );
+
+ throw new Error(`Input #${inputIndex} is not of type Taproot.`);
+ });
+ }
+
+ private _signInputAsync(
+ inputIndex: number,
+ keyPair: Signer | SignerAsync,
+ sighashTypes: number[] = [Transaction.SIGHASH_ALL],
+ ): Promise {
+ const { hash, sighashType } = getHashAndSighashType(
+ this.data.inputs,
+ inputIndex,
+ keyPair.publicKey,
+ this.__CACHE,
+ sighashTypes,
+ );
+
+ return Promise.resolve(keyPair.sign(hash)).then(signature => {
+ const partialSig = [
+ {
+ pubkey: keyPair.publicKey,
+ signature: bscript.signature.encode(signature, sighashType),
+ },
+ ];
+
+ this.data.updateInput(inputIndex, { partialSig });
+ });
+ }
+
+ private async _signTaprootInputAsync(
+ inputIndex: number,
+ input: PsbtInput,
+ keyPair: Signer | SignerAsync,
+ tapLeafHash?: Uint8Array,
+ sighashTypes: number[] = [Transaction.SIGHASH_DEFAULT],
+ ): Promise {
+ const hashesForSig = this.checkTaprootHashesForSig(
+ inputIndex,
+ input,
+ keyPair,
+ tapLeafHash,
+ sighashTypes,
+ );
+
+ const signaturePromises: Promise<
+ { tapKeySig: Uint8Array } | { tapScriptSig: TapScriptSig[] }
+ >[] = [];
+ const tapKeyHash = hashesForSig.filter(h => !h.leafHash)[0];
+ if (tapKeyHash) {
+ const tapKeySigPromise = Promise.resolve(
+ keyPair.signSchnorr!(tapKeyHash.hash),
+ ).then(sig => {
+ return { tapKeySig: serializeTaprootSignature(sig, input.sighashType) };
+ });
+ signaturePromises.push(tapKeySigPromise);
+ }
- this.data.updateInput(inputIndex, { partialSig });
+ const tapScriptHashes = hashesForSig.filter(h => !!h.leafHash);
+ if (tapScriptHashes.length) {
+ const tapScriptSigPromises = tapScriptHashes.map(tsh => {
+ return Promise.resolve(keyPair.signSchnorr!(tsh.hash)).then(
+ signature => {
+ const tapScriptSig = [
+ {
+ pubkey: toXOnly(keyPair.publicKey),
+ signature: serializeTaprootSignature(
+ signature,
+ input.sighashType,
+ ),
+ leafHash: tsh.leafHash,
+ } as TapScriptSig,
+ ];
+ return { tapScriptSig };
+ },
+ );
});
+ signaturePromises.push(...tapScriptSigPromises);
+ }
+
+ return Promise.all(signaturePromises).then(results => {
+ results.forEach(v => this.data.updateInput(inputIndex, v));
});
}
- toBuffer(): Buffer {
+ private checkTaprootHashesForSig(
+ inputIndex: number,
+ input: PsbtInput,
+ keyPair: Signer | SignerAsync,
+ tapLeafHashToSign?: Uint8Array,
+ allowedSighashTypes?: number[],
+ ): { hash: Uint8Array; leafHash?: Uint8Array }[] {
+ if (typeof keyPair.signSchnorr !== 'function')
+ throw new Error(
+ `Need Schnorr Signer to sign taproot input #${inputIndex}.`,
+ );
+
+ const hashesForSig = getTaprootHashesForSigning(
+ inputIndex,
+ input,
+ this.data.inputs,
+ keyPair.publicKey,
+ this.__CACHE,
+ tapLeafHashToSign,
+ allowedSighashTypes,
+ );
+
+ if (!hashesForSig || !hashesForSig.length)
+ throw new Error(
+ `Can not sign for input #${inputIndex} with the key ${tools.toHex(
+ keyPair.publicKey,
+ )}`,
+ );
+
+ return hashesForSig;
+ }
+
+ toBuffer(): Uint8Array {
checkCache(this.__CACHE);
return this.data.toBuffer();
}
@@ -704,6 +1098,11 @@ export class Psbt {
updateInput(inputIndex: number, updateData: PsbtInputUpdate): this {
if (updateData.witnessScript) checkInvalidP2WSH(updateData.witnessScript);
+ checkTaprootInputFields(
+ this.data.inputs[inputIndex],
+ updateData,
+ 'updateInput',
+ );
this.data.updateInput(inputIndex, updateData);
if (updateData.nonWitnessUtxo) {
addNonWitnessTxCache(
@@ -716,6 +1115,9 @@ export class Psbt {
}
updateOutput(outputIndex: number, updateData: PsbtOutputUpdate): this {
+ const outputData = this.data.outputs[outputIndex];
+ checkTaprootOutputFields(outputData, updateData, 'updateOutput');
+
this.data.updateOutput(outputIndex, updateData);
return this;
}
@@ -743,11 +1145,11 @@ export class Psbt {
interface PsbtCache {
__NON_WITNESS_UTXO_TX_CACHE: Transaction[];
- __NON_WITNESS_UTXO_BUF_CACHE: Buffer[];
+ __NON_WITNESS_UTXO_BUF_CACHE: Uint8Array[];
__TX_IN_CACHE: { [index: string]: number };
__TX: Transaction;
__FEE_RATE?: number;
- __FEE?: number;
+ __FEE?: bigint;
__EXTRACTED_TX?: Transaction;
__UNSAFE_SIGN_NONSEGWIT: boolean;
}
@@ -768,23 +1170,23 @@ type PsbtOutputExtended = PsbtOutputExtendedAddress | PsbtOutputExtendedScript;
interface PsbtOutputExtendedAddress extends PsbtOutput {
address: string;
- value: number;
+ value: bigint;
}
interface PsbtOutputExtendedScript extends PsbtOutput {
- script: Buffer;
- value: number;
+ script: Uint8Array;
+ value: bigint;
}
interface HDSignerBase {
/**
* DER format compressed publicKey buffer
*/
- publicKey: Buffer;
+ publicKey: Uint8Array;
/**
* The first 4 bytes of the sha256-ripemd160 of the publicKey
*/
- fingerprint: Buffer;
+ fingerprint: Uint8Array;
}
export interface HDSigner extends HDSignerBase {
@@ -797,7 +1199,7 @@ export interface HDSigner extends HDSignerBase {
* Input hash (the "message digest") for the signature algorithm
* Return a 64 byte signature (32 byte r and 32 byte s in that order)
*/
- sign(hash: Buffer): Buffer;
+ sign(hash: Uint8Array): Uint8Array;
}
/**
@@ -805,21 +1207,23 @@ export interface HDSigner extends HDSignerBase {
*/
export interface HDSignerAsync extends HDSignerBase {
derivePath(path: string): HDSignerAsync;
- sign(hash: Buffer): Promise;
+ sign(hash: Uint8Array): Promise;
}
export interface Signer {
- publicKey: Buffer;
+ publicKey: Uint8Array;
network?: any;
- sign(hash: Buffer, lowR?: boolean): Buffer;
- getPublicKey?(): Buffer;
+ sign(hash: Uint8Array, lowR?: boolean): Uint8Array;
+ signSchnorr?(hash: Uint8Array): Uint8Array;
+ getPublicKey?(): Uint8Array;
}
export interface SignerAsync {
- publicKey: Buffer;
+ publicKey: Uint8Array;
network?: any;
- sign(hash: Buffer, lowR?: boolean): Promise;
- getPublicKey?(): Buffer;
+ sign(hash: Uint8Array, lowR?: boolean): Promise;
+ signSchnorr?(hash: Uint8Array): Promise;
+ getPublicKey?(): Uint8Array;
}
/**
@@ -828,7 +1232,7 @@ export interface SignerAsync {
* Transaction (From the bip174 library) interface.
*/
const transactionFromBuffer: TransactionFromBuffer = (
- buffer: Buffer,
+ buffer: Uint8Array,
): ITransaction => new PsbtTransaction(buffer);
/**
@@ -837,7 +1241,9 @@ const transactionFromBuffer: TransactionFromBuffer = (
*/
class PsbtTransaction implements ITransaction {
tx: Transaction;
- constructor(buffer: Buffer = Buffer.from([2, 0, 0, 0, 0, 0, 0, 0, 0, 0])) {
+ constructor(
+ buffer: Uint8Array = Uint8Array.from([2, 0, 0, 0, 0, 0, 0, 0, 0, 0]),
+ ) {
this.tx = Transaction.fromBuffer(buffer);
checkTxEmpty(this.tx);
Object.defineProperty(this, 'tx', {
@@ -860,7 +1266,7 @@ class PsbtTransaction implements ITransaction {
if (
(input as any).hash === undefined ||
(input as any).index === undefined ||
- (!Buffer.isBuffer((input as any).hash) &&
+ (!((input as any).hash instanceof Uint8Array) &&
typeof (input as any).hash !== 'string') ||
typeof (input as any).index !== 'number'
) {
@@ -868,7 +1274,7 @@ class PsbtTransaction implements ITransaction {
}
const hash =
typeof input.hash === 'string'
- ? reverseBuffer(Buffer.from(input.hash, 'hex'))
+ ? reverseBuffer(tools.fromHex(input.hash))
: input.hash;
this.tx.addInput(hash, input.index, input.sequence);
}
@@ -877,22 +1283,22 @@ class PsbtTransaction implements ITransaction {
if (
(output as any).script === undefined ||
(output as any).value === undefined ||
- !Buffer.isBuffer((output as any).script) ||
- typeof (output as any).value !== 'number'
+ !((output as any).script instanceof Uint8Array) ||
+ typeof (output as any).value !== 'bigint'
) {
throw new Error('Error adding output.');
}
this.tx.addOutput(output.script, output.value);
}
- toBuffer(): Buffer {
+ toBuffer(): Uint8Array {
return this.tx.toBuffer();
}
}
function canFinalize(
input: PsbtInput,
- script: Buffer,
+ script: Uint8Array,
scriptType: string,
): boolean {
switch (scriptType) {
@@ -917,7 +1323,7 @@ function checkCache(cache: PsbtCache): void {
function hasSigs(
neededSigs: number,
partialSig?: any[],
- pubkeys?: Buffer[],
+ pubkeys?: Uint8Array[],
): boolean {
if (!partialSig) return false;
let sigs: any;
@@ -925,7 +1331,9 @@ function hasSigs(
sigs = pubkeys
.map(pkey => {
const pubkey = compressPubkey(pkey);
- return partialSig.find(pSig => pSig.pubkey.equals(pubkey));
+ return partialSig.find(
+ pSig => tools.compare(pSig.pubkey, pubkey) === 0,
+ );
})
.filter(v => !!v);
} else {
@@ -939,29 +1347,13 @@ function isFinalized(input: PsbtInput): boolean {
return !!input.finalScriptSig || !!input.finalScriptWitness;
}
-function isPaymentFactory(payment: any): (script: Buffer) => boolean {
- return (script: Buffer): boolean => {
- try {
- payment({ output: script });
- return true;
- } catch (err) {
- return false;
- }
- };
-}
-const isP2MS = isPaymentFactory(payments.p2ms);
-const isP2PK = isPaymentFactory(payments.p2pk);
-const isP2PKH = isPaymentFactory(payments.p2pkh);
-const isP2WPKH = isPaymentFactory(payments.p2wpkh);
-const isP2WSHScript = isPaymentFactory(payments.p2wsh);
-const isP2SHScript = isPaymentFactory(payments.p2sh);
-
function bip32DerivationIsMine(
root: HDSigner,
): (d: Bip32Derivation) => boolean {
return (d: Bip32Derivation): boolean => {
- if (!d.masterFingerprint.equals(root.fingerprint)) return false;
- if (!root.derivePath(d.path).publicKey.equals(d.pubkey)) return false;
+ if (tools.compare(root.fingerprint, d.masterFingerprint)) return false;
+ if (tools.compare(root.derivePath(d.path).publicKey, d.pubkey))
+ return false;
return true;
};
}
@@ -994,43 +1386,18 @@ function checkFees(psbt: Psbt, cache: PsbtCache, opts: PsbtOpts): void {
function checkInputsForPartialSig(inputs: PsbtInput[], action: string): void {
inputs.forEach(input => {
- let throws = false;
- let pSigs: PartialSig[] = [];
- if ((input.partialSig || []).length === 0) {
- if (!input.finalScriptSig && !input.finalScriptWitness) return;
- pSigs = getPsigsFromInputFinalScripts(input);
- } else {
- pSigs = input.partialSig!;
- }
- pSigs.forEach(pSig => {
- const { hashType } = bscript.signature.decode(pSig.signature);
- const whitelist: string[] = [];
- const isAnyoneCanPay = hashType & Transaction.SIGHASH_ANYONECANPAY;
- if (isAnyoneCanPay) whitelist.push('addInput');
- const hashMod = hashType & 0x1f;
- switch (hashMod) {
- case Transaction.SIGHASH_ALL:
- break;
- case Transaction.SIGHASH_SINGLE:
- case Transaction.SIGHASH_NONE:
- whitelist.push('addOutput');
- whitelist.push('setInputSequence');
- break;
- }
- if (whitelist.indexOf(action) === -1) {
- throws = true;
- }
- });
- if (throws) {
+ const throws = isTaprootInput(input)
+ ? checkTaprootInputForSigs(input, action)
+ : checkInputForSig(input, action);
+ if (throws)
throw new Error('Can not modify transaction, signatures exist.');
- }
});
}
function checkPartialSigSighashes(input: PsbtInput): void {
if (!input.sighashType || !input.partialSig) return;
const { partialSig, sighashType } = input;
- partialSig.forEach(pSig => {
+ partialSig.forEach((pSig: PartialSig) => {
const { hashType } = bscript.signature.decode(pSig.signature);
if (sighashType !== hashType) {
throw new Error('Signature sighash does not match input sighash type');
@@ -1039,13 +1406,13 @@ function checkPartialSigSighashes(input: PsbtInput): void {
}
function checkScriptForPubkey(
- pubkey: Buffer,
- script: Buffer,
+ pubkey: Uint8Array,
+ script: Uint8Array,
action: string,
): void {
if (!pubkeyInScript(pubkey, script)) {
throw new Error(
- `Can not ${action} for this input with the key ${pubkey.toString('hex')}`,
+ `Can not ${action} for this input with the key ${tools.toHex(pubkey)}`,
);
}
}
@@ -1071,10 +1438,10 @@ function checkTxForDupeIns(tx: Transaction, cache: PsbtCache): void {
function checkTxInputCache(
cache: PsbtCache,
- input: { hash: Buffer; index: number },
+ input: { hash: Uint8Array; index: number },
): void {
const key =
- reverseBuffer(Buffer.from(input.hash)).toString('hex') + ':' + input.index;
+ tools.toHex(reverseBuffer(Uint8Array.from(input.hash))) + ':' + input.index;
if (cache.__TX_IN_CACHE[key]) throw new Error('Duplicate input detected.');
cache.__TX_IN_CACHE[key] = 1;
}
@@ -1082,18 +1449,23 @@ function checkTxInputCache(
function scriptCheckerFactory(
payment: any,
paymentScriptName: string,
-): (idx: number, spk: Buffer, rs: Buffer, ioType: 'input' | 'output') => void {
+): (
+ idx: number,
+ spk: Uint8Array,
+ rs: Uint8Array,
+ ioType: 'input' | 'output',
+) => void {
return (
inputIndex: number,
- scriptPubKey: Buffer,
- redeemScript: Buffer,
+ scriptPubKey: Uint8Array,
+ redeemScript: Uint8Array,
ioType: 'input' | 'output',
): void => {
const redeemScriptOutput = payment({
redeem: { output: redeemScript },
- }).output as Buffer;
+ }).output as Uint8Array;
- if (!scriptPubKey.equals(redeemScriptOutput)) {
+ if (tools.compare(scriptPubKey, redeemScriptOutput)) {
throw new Error(
`${paymentScriptName} for ${ioType} #${inputIndex} doesn't match the scriptPubKey in the prevout`,
);
@@ -1107,12 +1479,12 @@ const checkWitnessScript = scriptCheckerFactory(
);
type TxCacheNumberKey = '__FEE_RATE' | '__FEE';
-function getTxCacheValue(
- key: TxCacheNumberKey,
+function getTxCacheValue(
+ key: T,
name: string,
inputs: PsbtInput[],
c: PsbtCache,
-): number | undefined {
+): bigint | number | undefined {
if (!inputs.every(isFinalized))
throw new Error(`PSBT must be finalized to calculate ${name}`);
if (key === '__FEE_RATE' && c.__FEE_RATE) return c.__FEE_RATE;
@@ -1139,25 +1511,32 @@ function getTxCacheValue(
type FinalScriptsFunc = (
inputIndex: number, // Which input is it?
input: PsbtInput, // The PSBT input contents
- script: Buffer, // The "meaningful" locking script Buffer (redeemScript for P2SH etc.)
+ script: Uint8Array, // The "meaningful" locking script Buffer (redeemScript for P2SH etc.)
isSegwit: boolean, // Is it segwit?
isP2SH: boolean, // Is it P2SH?
isP2WSH: boolean, // Is it P2WSH?
) => {
- finalScriptSig: Buffer | undefined;
- finalScriptWitness: Buffer | undefined;
+ finalScriptSig: Uint8Array | undefined;
+ finalScriptWitness: Uint8Array | undefined;
+};
+type FinalTaprootScriptsFunc = (
+ inputIndex: number, // Which input is it?
+ input: PsbtInput, // The PSBT input contents
+ tapLeafHashToFinalize?: Uint8Array, // Only finalize this specific leaf
+) => {
+ finalScriptWitness: Uint8Array | undefined;
};
function getFinalScripts(
inputIndex: number,
input: PsbtInput,
- script: Buffer,
+ script: Uint8Array,
isSegwit: boolean,
isP2SH: boolean,
isP2WSH: boolean,
): {
- finalScriptSig: Buffer | undefined;
- finalScriptWitness: Buffer | undefined;
+ finalScriptSig: Uint8Array | undefined;
+ finalScriptWitness: Uint8Array | undefined;
} {
const scriptType = classifyScript(script);
if (!canFinalize(input, script, scriptType))
@@ -1173,18 +1552,18 @@ function getFinalScripts(
}
function prepareFinalScripts(
- script: Buffer,
+ script: Uint8Array,
scriptType: string,
partialSig: PartialSig[],
isSegwit: boolean,
isP2SH: boolean,
isP2WSH: boolean,
): {
- finalScriptSig: Buffer | undefined;
- finalScriptWitness: Buffer | undefined;
+ finalScriptSig: Uint8Array | undefined;
+ finalScriptWitness: Uint8Array | undefined;
} {
- let finalScriptSig: Buffer | undefined;
- let finalScriptWitness: Buffer | undefined;
+ let finalScriptSig: Uint8Array | undefined;
+ let finalScriptWitness: Uint8Array | undefined;
// Wow, the payments API is very handy
const payment: payments.Payment = getPayment(script, scriptType, partialSig);
@@ -1216,11 +1595,11 @@ function prepareFinalScripts(
function getHashAndSighashType(
inputs: PsbtInput[],
inputIndex: number,
- pubkey: Buffer,
+ pubkey: Uint8Array,
cache: PsbtCache,
sighashTypes: number[],
): {
- hash: Buffer;
+ hash: Uint8Array;
sighashType: number;
} {
const input = checkForInput(inputs, inputIndex);
@@ -1245,20 +1624,15 @@ function getHashForSig(
forValidate: boolean,
sighashTypes?: number[],
): {
- script: Buffer;
- hash: Buffer;
+ script: Uint8Array;
+ hash: Uint8Array;
sighashType: number;
} {
const unsignedTx = cache.__TX;
const sighashType = input.sighashType || Transaction.SIGHASH_ALL;
- if (sighashTypes && sighashTypes.indexOf(sighashType) < 0) {
- const str = sighashTypeToString(sighashType);
- throw new Error(
- `Sighash type is not allowed. Retry the sign method passing the ` +
- `sighashTypes array of whitelisted types. Sighash type: ${str}`,
- );
- }
- let hash: Buffer;
+ checkSighashTypeAllowed(sighashType, sighashTypes);
+
+ let hash: Uint8Array;
let prevout: Output;
if (input.nonWitnessUtxo) {
@@ -1272,7 +1646,7 @@ function getHashForSig(
const utxoHash = nonWitnessUtxoTx.getHash();
// If a non-witness UTXO is provided, its hash must match the hash specified in the prevout
- if (!prevoutHash.equals(utxoHash)) {
+ if (tools.compare(prevoutHash, utxoHash) !== 0) {
throw new Error(
`Non-witness UTXO hash for input #${inputIndex} doesn't match the hash specified in the prevout`,
);
@@ -1303,8 +1677,9 @@ function getHashForSig(
);
} else if (isP2WPKH(meaningfulScript)) {
// P2WPKH uses the P2PKH template for prevoutScript when signing
- const signingScript = payments.p2pkh({ hash: meaningfulScript.slice(2) })
- .output!;
+ const signingScript = payments.p2pkh({
+ hash: meaningfulScript.slice(2),
+ }).output!;
hash = unsignedTx.hashForWitnessV0(
inputIndex,
signingScript,
@@ -1319,13 +1694,13 @@ function getHashForSig(
)
throw new Error(
`Input #${inputIndex} has witnessUtxo but non-segwit script: ` +
- `${meaningfulScript.toString('hex')}`,
+ `${tools.toHex(meaningfulScript)}`,
);
if (!forValidate && cache.__UNSAFE_SIGN_NONSEGWIT !== false)
console.warn(
'Warning: Signing non-segwit inputs without the full parent transaction ' +
'means there is a chance that a miner could feed you incorrect information ' +
- "to trick you into paying large fees. This behavior is the same as Psbt's predecesor " +
+ "to trick you into paying large fees. This behavior is the same as Psbt's predecessor " +
'(TransactionBuilder - now removed) when signing non-segwit scripts. You are not ' +
'able to export this Psbt with toBuffer|toBase64|toHex since it is not ' +
'BIP174 compliant.\n*********************\nPROCEED WITH CAUTION!\n' +
@@ -1345,8 +1720,185 @@ function getHashForSig(
};
}
+function getAllTaprootHashesForSigValidation(
+ inputIndex: number,
+ input: PsbtInput,
+ inputs: PsbtInput[],
+ cache: PsbtCache,
+): { pubkey: Uint8Array; hash: Uint8Array; leafHash?: Uint8Array }[] {
+ const allPublicKeys = [];
+ if (input.tapInternalKey) {
+ const key = getPrevoutTaprootKey(inputIndex, input, cache);
+ if (key) {
+ allPublicKeys.push(key);
+ }
+ }
+
+ if (input.tapScriptSig) {
+ const tapScriptPubkeys = input.tapScriptSig.map(
+ (tss: TapScriptSig) => tss.pubkey,
+ );
+ allPublicKeys.push(...tapScriptPubkeys);
+ }
+
+ const allHashes = allPublicKeys.map(publicKey =>
+ getTaprootHashesForSigValidation(
+ inputIndex,
+ input,
+ inputs,
+ publicKey,
+ cache,
+ ),
+ );
+
+ return allHashes.flat();
+}
+
+function getPrevoutTaprootKey(
+ inputIndex: number,
+ input: PsbtInput,
+ cache: PsbtCache,
+): Uint8Array | null {
+ const { script } = getScriptAndAmountFromUtxo(inputIndex, input, cache);
+ return isP2TR(script) ? script.subarray(2, 34) : null;
+}
+
+function trimTaprootSig(signature: Uint8Array): Uint8Array {
+ return signature.length === 64 ? signature : signature.subarray(0, 64);
+}
+
+function getTaprootHashesForSigning(
+ inputIndex: number,
+ input: PsbtInput,
+ inputs: PsbtInput[],
+ pubkey: Uint8Array,
+ cache: PsbtCache,
+ tapLeafHashToSign?: Uint8Array,
+ allowedSighashTypes?: number[],
+): { pubkey: Uint8Array; hash: Uint8Array; leafHash?: Uint8Array }[] {
+ const sighashType = input.sighashType || Transaction.SIGHASH_DEFAULT;
+ checkSighashTypeAllowed(sighashType, allowedSighashTypes);
+
+ const keySpend = Boolean(input.tapInternalKey && !tapLeafHashToSign);
+
+ return getTaprootHashesForSig(
+ inputIndex,
+ input,
+ inputs,
+ pubkey,
+ cache,
+ keySpend,
+ sighashType,
+ tapLeafHashToSign,
+ );
+}
+
+function getTaprootHashesForSigValidation(
+ inputIndex: number,
+ input: PsbtInput,
+ inputs: PsbtInput[],
+ pubkey: Uint8Array,
+ cache: PsbtCache,
+): { pubkey: Uint8Array; hash: Uint8Array; leafHash?: Uint8Array }[] {
+ const sighashType = input.sighashType || Transaction.SIGHASH_DEFAULT;
+ const keySpend = Boolean(input.tapKeySig);
+ return getTaprootHashesForSig(
+ inputIndex,
+ input,
+ inputs,
+ pubkey,
+ cache,
+ keySpend,
+ sighashType,
+ );
+}
+
+/*
+ * This helper method is used for both generating a hash for signing as well for validating;
+ * thus depending on context key spend can be detected either with tapInternalKey with no leaves
+ * or tapKeySig (note tapKeySig is a signature to the prevout pk, so tapInternalKey is not needed).
+ */
+function getTaprootHashesForSig(
+ inputIndex: number,
+ input: PsbtInput,
+ inputs: PsbtInput[],
+ pubkey: Uint8Array,
+ cache: PsbtCache,
+ keySpend: boolean,
+ sighashType: number,
+ tapLeafHashToSign?: Uint8Array,
+): { pubkey: Uint8Array; hash: Uint8Array; leafHash?: Uint8Array }[] {
+ const unsignedTx = cache.__TX;
+
+ const prevOuts: Output[] = inputs.map((i, index) =>
+ getScriptAndAmountFromUtxo(index, i, cache),
+ );
+ const signingScripts = prevOuts.map(o => o.script);
+ const values = prevOuts.map(o => o.value);
+
+ const hashes = [];
+ if (keySpend) {
+ const outputKey =
+ getPrevoutTaprootKey(inputIndex, input, cache) || Uint8Array.from([]);
+ if (tools.compare(toXOnly(pubkey), outputKey) === 0) {
+ const tapKeyHash = unsignedTx.hashForWitnessV1(
+ inputIndex,
+ signingScripts,
+ values,
+ sighashType,
+ );
+ hashes.push({ pubkey, hash: tapKeyHash });
+ }
+ }
+
+ const tapLeafHashes = (input.tapLeafScript || [])
+ .filter((tapLeaf: TapLeafScript) => pubkeyInScript(pubkey, tapLeaf.script))
+ .map((tapLeaf: TapLeafScript) => {
+ const hash = tapleafHash({
+ output: tapLeaf.script,
+ version: tapLeaf.leafVersion,
+ });
+ return Object.assign({ hash }, tapLeaf);
+ })
+ .filter(
+ tapLeaf =>
+ !tapLeafHashToSign ||
+ tools.compare(tapLeafHashToSign, tapLeaf.hash) === 0,
+ )
+ .map(tapLeaf => {
+ const tapScriptHash = unsignedTx.hashForWitnessV1(
+ inputIndex,
+ signingScripts,
+ values,
+ sighashType,
+ tapLeaf.hash,
+ );
+
+ return {
+ pubkey,
+ hash: tapScriptHash,
+ leafHash: tapLeaf.hash,
+ };
+ });
+
+ return hashes.concat(tapLeafHashes);
+}
+
+function checkSighashTypeAllowed(
+ sighashType: number,
+ sighashTypes?: number[],
+): void {
+ if (sighashTypes && sighashTypes.indexOf(sighashType) < 0) {
+ const str = sighashTypeToString(sighashType);
+ throw new Error(
+ `Sighash type is not allowed. Retry the sign method passing the ` +
+ `sighashTypes array of whitelisted types. Sighash type: ${str}`,
+ );
+ }
+}
+
function getPayment(
- script: Buffer,
+ script: Uint8Array,
scriptType: string,
partialSig: PartialSig[],
): payments.Payment {
@@ -1383,23 +1935,8 @@ function getPayment(
return payment!;
}
-function getPsigsFromInputFinalScripts(input: PsbtInput): PartialSig[] {
- const scriptItems = !input.finalScriptSig
- ? []
- : bscript.decompile(input.finalScriptSig) || [];
- const witnessItems = !input.finalScriptWitness
- ? []
- : bscript.decompile(input.finalScriptWitness) || [];
- return scriptItems
- .concat(witnessItems)
- .filter(item => {
- return Buffer.isBuffer(item) && bscript.isCanonicalScriptSignature(item);
- })
- .map(sig => ({ signature: sig })) as PartialSig[];
-}
-
interface GetScriptReturn {
- script: Buffer | null;
+ script: Uint8Array | null;
isSegwit: boolean;
isP2SH: boolean;
isP2WSH: boolean;
@@ -1451,14 +1988,14 @@ function getSignersFromHD(
throw new Error('Need bip32Derivation to sign with HD');
}
const myDerivations = input.bip32Derivation
- .map(bipDv => {
- if (bipDv.masterFingerprint.equals(hdKeyPair.fingerprint)) {
+ .map((bipDv: Bip32Derivation) => {
+ if (tools.compare(bipDv.masterFingerprint, hdKeyPair.fingerprint) === 0) {
return bipDv;
} else {
return;
}
})
- .filter(v => !!v);
+ .filter((v: Bip32Derivation | undefined) => !!v) as Bip32Derivation[];
if (myDerivations.length === 0) {
throw new Error(
'Need one bip32Derivation masterFingerprint to match the HDSigner fingerprint',
@@ -1466,7 +2003,7 @@ function getSignersFromHD(
}
const signers: Array = myDerivations.map(bipDv => {
const node = hdKeyPair.derivePath(bipDv!.path);
- if (!bipDv!.pubkey.equals(node.publicKey)) {
+ if (tools.compare(bipDv!.pubkey, node.publicKey) !== 0) {
throw new Error('pubkey did not match bip32Derivation');
}
return node;
@@ -1474,7 +2011,10 @@ function getSignersFromHD(
return signers;
}
-function getSortedSigs(script: Buffer, partialSig: PartialSig[]): Buffer[] {
+function getSortedSigs(
+ script: Uint8Array,
+ partialSig: PartialSig[],
+): Uint8Array[] {
const p2ms = payments.p2ms({ output: script });
// for each pubkey in order of p2ms script
return p2ms
@@ -1482,7 +2022,7 @@ function getSortedSigs(script: Buffer, partialSig: PartialSig[]): Buffer[] {
// filter partialSig array by pubkey being equal
return (
partialSig.filter(ps => {
- return ps.pubkey.equals(pk);
+ return tools.compare(ps.pubkey, pk) === 0;
})[0] || {}
).signature;
// Any pubkey without a match will return undefined
@@ -1491,27 +2031,27 @@ function getSortedSigs(script: Buffer, partialSig: PartialSig[]): Buffer[] {
.filter(v => !!v);
}
-function scriptWitnessToWitnessStack(buffer: Buffer): Buffer[] {
+function scriptWitnessToWitnessStack(buffer: Uint8Array): Uint8Array[] {
let offset = 0;
- function readSlice(n: number): Buffer {
+ function readSlice(n: number): Uint8Array {
offset += n;
return buffer.slice(offset - n, offset);
}
function readVarInt(): number {
const vi = varuint.decode(buffer, offset);
- offset += (varuint.decode as any).bytes;
- return vi;
+ offset += varuint.encodingLength(vi.bigintValue);
+ return vi.numberValue!;
}
- function readVarSlice(): Buffer {
+ function readVarSlice(): Uint8Array {
return readSlice(readVarInt());
}
- function readVector(): Buffer[] {
+ function readVector(): Uint8Array[] {
const count = readVarInt();
- const vector: Buffer[] = [];
+ const vector: Uint8Array[] = [];
for (let i = 0; i < count; i++) vector.push(readVarSlice());
return vector;
}
@@ -1539,36 +2079,6 @@ function sighashTypeToString(sighashType: number): string {
return text;
}
-function witnessStackToScriptWitness(witness: Buffer[]): Buffer {
- let buffer = Buffer.allocUnsafe(0);
-
- function writeSlice(slice: Buffer): void {
- buffer = Buffer.concat([buffer, Buffer.from(slice)]);
- }
-
- function writeVarInt(i: number): void {
- const currentLen = buffer.length;
- const varintLen = varuint.encodingLength(i);
-
- buffer = Buffer.concat([buffer, Buffer.allocUnsafe(varintLen)]);
- varuint.encode(i, buffer, currentLen);
- }
-
- function writeVarSlice(slice: Buffer): void {
- writeVarInt(slice.length);
- writeSlice(slice);
- }
-
- function writeVector(vector: Buffer[]): void {
- writeVarInt(vector.length);
- vector.forEach(writeVarSlice);
- }
-
- writeVector(witness);
-
- return buffer;
-}
-
function addNonWitnessTxCache(
cache: PsbtCache,
input: PsbtInput,
@@ -1584,7 +2094,7 @@ function addNonWitnessTxCache(
delete input.nonWitnessUtxo;
Object.defineProperty(input, 'nonWitnessUtxo', {
enumerable: true,
- get(): Buffer {
+ get(): Uint8Array {
const buf = self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex];
const txCache = self.__NON_WITNESS_UTXO_TX_CACHE[selfIndex];
if (buf !== undefined) {
@@ -1595,7 +2105,7 @@ function addNonWitnessTxCache(
return newBuf;
}
},
- set(data: Buffer): void {
+ set(data: Uint8Array): void {
self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex] = data;
},
});
@@ -1607,7 +2117,7 @@ function inputFinalizeGetAmts(
cache: PsbtCache,
mustFinalize: boolean,
): void {
- let inputAmount = 0;
+ let inputAmount = 0n;
inputs.forEach((input, idx) => {
if (mustFinalize && input.finalScriptSig)
tx.ins[idx].script = input.finalScriptSig;
@@ -1627,7 +2137,7 @@ function inputFinalizeGetAmts(
});
const outputAmount = (tx.outs as Output[]).reduce(
(total, o) => total + o.value,
- 0,
+ 0n,
);
const fee = inputAmount - outputAmount;
if (fee < 0) {
@@ -1636,7 +2146,7 @@ function inputFinalizeGetAmts(
const bytes = tx.virtualSize();
cache.__FEE = fee;
cache.__EXTRACTED_TX = tx;
- cache.__FEE_RATE = Math.floor(fee / bytes);
+ cache.__FEE_RATE = Math.floor(Number(fee / BigInt(bytes)));
}
function nonWitnessUtxoTxFromCache(
@@ -1655,23 +2165,36 @@ function getScriptFromUtxo(
inputIndex: number,
input: PsbtInput,
cache: PsbtCache,
-): Buffer {
+): Uint8Array {
+ const { script } = getScriptAndAmountFromUtxo(inputIndex, input, cache);
+ return script;
+}
+
+function getScriptAndAmountFromUtxo(
+ inputIndex: number,
+ input: PsbtInput,
+ cache: PsbtCache,
+): { script: Uint8Array; value: bigint } {
if (input.witnessUtxo !== undefined) {
- return input.witnessUtxo.script;
+ return {
+ script: input.witnessUtxo.script,
+ value: input.witnessUtxo.value,
+ };
} else if (input.nonWitnessUtxo !== undefined) {
const nonWitnessUtxoTx = nonWitnessUtxoTxFromCache(
cache,
input,
inputIndex,
);
- return nonWitnessUtxoTx.outs[cache.__TX.ins[inputIndex].index].script;
+ const o = nonWitnessUtxoTx.outs[cache.__TX.ins[inputIndex].index];
+ return { script: o.script, value: o.value };
} else {
throw new Error("Can't find pubkey in input without Utxo data");
}
}
function pubkeyInInput(
- pubkey: Buffer,
+ pubkey: Uint8Array,
input: PsbtInput,
inputIndex: number,
cache: PsbtCache,
@@ -1688,7 +2211,7 @@ function pubkeyInInput(
}
function pubkeyInOutput(
- pubkey: Buffer,
+ pubkey: Uint8Array,
output: PsbtOutput,
outputIndex: number,
cache: PsbtCache,
@@ -1705,14 +2228,14 @@ function pubkeyInOutput(
}
function redeemFromFinalScriptSig(
- finalScript: Buffer | undefined,
-): Buffer | undefined {
+ finalScript: Uint8Array | undefined,
+): Uint8Array | undefined {
if (!finalScript) return;
const decomp = bscript.decompile(finalScript);
if (!decomp) return;
const lastItem = decomp[decomp.length - 1];
if (
- !Buffer.isBuffer(lastItem) ||
+ !(lastItem instanceof Uint8Array) ||
isPubkeyLike(lastItem) ||
isSigLike(lastItem)
)
@@ -1723,8 +2246,8 @@ function redeemFromFinalScriptSig(
}
function redeemFromFinalWitnessScript(
- finalScript: Buffer | undefined,
-): Buffer | undefined {
+ finalScript: Uint8Array | undefined,
+): Uint8Array | undefined {
if (!finalScript) return;
const decomp = scriptWitnessToWitnessStack(finalScript);
const lastItem = decomp[decomp.length - 1];
@@ -1734,7 +2257,7 @@ function redeemFromFinalWitnessScript(
return lastItem;
}
-function compressPubkey(pubkey: Buffer): Buffer {
+function compressPubkey(pubkey: Uint8Array): Uint8Array {
if (pubkey.length === 65) {
const parity = pubkey[64] & 1;
const newKey = pubkey.slice(0, 33);
@@ -1744,22 +2267,22 @@ function compressPubkey(pubkey: Buffer): Buffer {
return pubkey.slice();
}
-function isPubkeyLike(buf: Buffer): boolean {
+function isPubkeyLike(buf: Uint8Array): boolean {
return buf.length === 33 && bscript.isCanonicalPubKey(buf);
}
-function isSigLike(buf: Buffer): boolean {
+function isSigLike(buf: Uint8Array): boolean {
return bscript.isCanonicalScriptSignature(buf);
}
function getMeaningfulScript(
- script: Buffer,
+ script: Uint8Array,
index: number,
ioType: 'input' | 'output',
- redeemScript?: Buffer,
- witnessScript?: Buffer,
+ redeemScript?: Uint8Array,
+ witnessScript?: Uint8Array,
): {
- meaningfulScript: Buffer;
+ meaningfulScript: Uint8Array;
type: 'p2sh' | 'p2wsh' | 'p2sh-p2wsh' | 'raw';
} {
const isP2SH = isP2SHScript(script);
@@ -1773,7 +2296,7 @@ function getMeaningfulScript(
'scriptPubkey or redeemScript is P2WSH but witnessScript missing',
);
- let meaningfulScript: Buffer;
+ let meaningfulScript: Uint8Array;
if (isP2SHP2WSH) {
meaningfulScript = witnessScript!;
@@ -1795,31 +2318,19 @@ function getMeaningfulScript(
type: isP2SHP2WSH
? 'p2sh-p2wsh'
: isP2SH
- ? 'p2sh'
- : isP2WSH
- ? 'p2wsh'
- : 'raw',
+ ? 'p2sh'
+ : isP2WSH
+ ? 'p2wsh'
+ : 'raw',
};
}
-function checkInvalidP2WSH(script: Buffer): void {
+function checkInvalidP2WSH(script: Uint8Array): void {
if (isP2WPKH(script) || isP2SHScript(script)) {
throw new Error('P2WPKH or P2SH can not be contained within P2WSH');
}
}
-function pubkeyInScript(pubkey: Buffer, script: Buffer): boolean {
- const pubkeyHash = hash160(pubkey);
-
- const decompiled = bscript.decompile(script);
- if (decompiled === null) throw new Error('Unknown script error');
-
- return decompiled.some(element => {
- if (typeof element === 'number') return false;
- return element.equals(pubkey) || element.equals(pubkeyHash);
- });
-}
-
type AllScriptType =
| 'witnesspubkeyhash'
| 'pubkeyhash'
@@ -1845,7 +2356,7 @@ type ScriptType =
| 'multisig'
| 'pubkey'
| 'nonstandard';
-function classifyScript(script: Buffer): ScriptType {
+function classifyScript(script: Uint8Array): ScriptType {
if (isP2WPKH(script)) return 'witnesspubkeyhash';
if (isP2PKH(script)) return 'pubkeyhash';
if (isP2MS(script)) return 'multisig';
diff --git a/ts_src/psbt/bip371.ts b/ts_src/psbt/bip371.ts
new file mode 100644
index 000000000..4fd5271a5
--- /dev/null
+++ b/ts_src/psbt/bip371.ts
@@ -0,0 +1,622 @@
+import { Taptree, Tapleaf, isTapleaf, isTaptree } from '../types.js';
+import {
+ PsbtInput,
+ PsbtOutput,
+ TapLeafScript,
+ TapScriptSig,
+ TapLeaf,
+ TapTree,
+ TapInternalKey,
+} from 'bip174';
+
+import { Transaction } from '../transaction.js';
+
+import {
+ witnessStackToScriptWitness,
+ pubkeyPositionInScript,
+ isP2TR,
+} from './psbtutils.js';
+import {
+ tweakKey,
+ tapleafHash,
+ rootHashFromPath,
+ LEAF_VERSION_TAPSCRIPT,
+ MAX_TAPTREE_DEPTH,
+} from '../payments/bip341.js';
+import { p2tr } from '../payments/index.js';
+import * as tools from 'uint8array-tools';
+import { signatureBlocksAction } from './psbtutils.js';
+
+/**
+ * Converts a public key to an X-only public key.
+ * @param pubKey The public key to convert.
+ * @returns The X-only public key.
+ */
+export const toXOnly = (pubKey: Uint8Array) =>
+ pubKey.length === 32 ? pubKey : pubKey.slice(1, 33);
+
+/**
+ * Default tapscript finalizer. It searches for the `tapLeafHashToFinalize` if provided.
+ * Otherwise it will search for the tapleaf that has at least one signature and has the shortest path.
+ * @param inputIndex the position of the PSBT input.
+ * @param input the PSBT input.
+ * @param tapLeafHashToFinalize optional, if provided the finalizer will search for a tapleaf that has this hash
+ * and will try to build the finalScriptWitness.
+ * @returns the finalScriptWitness or throws an exception if no tapleaf found.
+ */
+export function tapScriptFinalizer(
+ inputIndex: number,
+ input: PsbtInput,
+ tapLeafHashToFinalize?: Uint8Array,
+): {
+ finalScriptWitness: Uint8Array | undefined;
+} {
+ const tapLeaf = findTapLeafToFinalize(
+ input,
+ inputIndex,
+ tapLeafHashToFinalize,
+ );
+
+ try {
+ const sigs = sortSignatures(input, tapLeaf);
+ const witness = sigs.concat(tapLeaf.script).concat(tapLeaf.controlBlock);
+ return { finalScriptWitness: witnessStackToScriptWitness(witness) };
+ } catch (err) {
+ throw new Error(`Can not finalize taproot input #${inputIndex}: ${err}`);
+ }
+}
+
+/**
+ * Serializes a taproot signature.
+ * @param sig The signature to serialize.
+ * @param sighashType The sighash type. Optional.
+ * @returns The serialized taproot signature.
+ */
+export function serializeTaprootSignature(
+ sig: Uint8Array,
+ sighashType?: number,
+): Uint8Array {
+ const sighashTypeByte = sighashType
+ ? Uint8Array.from([sighashType!])
+ : Uint8Array.from([]);
+
+ return tools.concat([sig, sighashTypeByte]);
+}
+
+/**
+ * Checks if a PSBT input is a taproot input.
+ * @param input The PSBT input to check.
+ * @returns True if the input is a taproot input, false otherwise.
+ */
+export function isTaprootInput(input: PsbtInput): boolean {
+ return (
+ input &&
+ !!(
+ input.tapInternalKey ||
+ input.tapMerkleRoot ||
+ (input.tapLeafScript && input.tapLeafScript.length) ||
+ (input.tapBip32Derivation && input.tapBip32Derivation.length) ||
+ (input.witnessUtxo && isP2TR(input.witnessUtxo.script))
+ )
+ );
+}
+
+/**
+ * Checks if a PSBT output is a taproot output.
+ * @param output The PSBT output to check.
+ * @param script The script to check. Optional.
+ * @returns True if the output is a taproot output, false otherwise.
+ */
+export function isTaprootOutput(
+ output: PsbtOutput,
+ script?: Uint8Array,
+): boolean {
+ return (
+ output &&
+ !!(
+ output.tapInternalKey ||
+ output.tapTree ||
+ (output.tapBip32Derivation && output.tapBip32Derivation.length) ||
+ (script && isP2TR(script))
+ )
+ );
+}
+
+/**
+ * Checks the taproot input fields for consistency.
+ * @param inputData The original input data.
+ * @param newInputData The new input data.
+ * @param action The action being performed.
+ * @throws Throws an error if the input fields are inconsistent.
+ */
+export function checkTaprootInputFields(
+ inputData: PsbtInput,
+ newInputData: PsbtInput,
+ action: string,
+): void {
+ checkMixedTaprootAndNonTaprootInputFields(inputData, newInputData, action);
+ checkIfTapLeafInTree(inputData, newInputData, action);
+}
+
+/**
+ * Checks the taproot output fields for consistency.
+ * @param outputData The original output data.
+ * @param newOutputData The new output data.
+ * @param action The action being performed.
+ * @throws Throws an error if the output fields are inconsistent.
+ */
+export function checkTaprootOutputFields(
+ outputData: PsbtOutput,
+ newOutputData: PsbtOutput,
+ action: string,
+): void {
+ checkMixedTaprootAndNonTaprootOutputFields(outputData, newOutputData, action);
+ checkTaprootScriptPubkey(outputData, newOutputData);
+}
+
+function checkTaprootScriptPubkey(
+ outputData: PsbtOutput,
+ newOutputData: PsbtOutput,
+): void {
+ if (!newOutputData.tapTree && !newOutputData.tapInternalKey) return;
+
+ const tapInternalKey =
+ newOutputData.tapInternalKey || outputData.tapInternalKey;
+ const tapTree = newOutputData.tapTree || outputData.tapTree;
+
+ if (tapInternalKey) {
+ const { script: scriptPubkey } = outputData as any;
+ const script = getTaprootScripPubkey(tapInternalKey, tapTree);
+ if (scriptPubkey && tools.compare(script, scriptPubkey) !== 0)
+ throw new Error('Error adding output. Script or address mismatch.');
+ }
+}
+
+/**
+ * Returns the Taproot script public key.
+ *
+ * @param tapInternalKey - The Taproot internal key.
+ * @param tapTree - The Taproot tree (optional).
+ * @returns The Taproot script public key.
+ */
+function getTaprootScripPubkey(
+ tapInternalKey: TapInternalKey,
+ tapTree?: TapTree,
+): Uint8Array {
+ const scriptTree = tapTree && tapTreeFromList(tapTree.leaves);
+ const { output } = p2tr({
+ internalPubkey: tapInternalKey,
+ scriptTree,
+ });
+ return output!;
+}
+
+/**
+ * Tweak the internal public key for a specific input.
+ * @param inputIndex - The index of the input.
+ * @param input - The PsbtInput object representing the input.
+ * @returns The tweaked internal public key.
+ * @throws Error if the tap internal key cannot be tweaked.
+ */
+export function tweakInternalPubKey(
+ inputIndex: number,
+ input: PsbtInput,
+): Uint8Array {
+ const tapInternalKey = input.tapInternalKey;
+ const outputKey =
+ tapInternalKey && tweakKey(tapInternalKey, input.tapMerkleRoot);
+
+ if (!outputKey)
+ throw new Error(
+ `Cannot tweak tap internal key for input #${inputIndex}. Public key: ${
+ // tapInternalKey && tapInternalKey.toString('hex')
+ tapInternalKey && tools.toHex(tapInternalKey)
+ }`,
+ );
+ return outputKey.x;
+}
+
+/**
+ * Convert a binary tree to a BIP371 type list. Each element of the list is (according to BIP371):
+ * One or more tuples representing the depth, leaf version, and script for a leaf in the Taproot tree,
+ * allowing the entire tree to be reconstructed. The tuples must be in depth first search order so that
+ * the tree is correctly reconstructed.
+ * @param tree the binary tap tree
+ * @returns a list of BIP 371 tapleaves
+ */
+export function tapTreeToList(tree: Taptree): TapLeaf[] {
+ if (!isTaptree(tree))
+ throw new Error(
+ 'Cannot convert taptree to tapleaf list. Expecting a tapree structure.',
+ );
+ return _tapTreeToList(tree);
+}
+
+/**
+ * Convert a BIP371 TapLeaf list to a TapTree (binary).
+ * @param leaves a list of tapleaves where each element of the list is (according to BIP371):
+ * One or more tuples representing the depth, leaf version, and script for a leaf in the Taproot tree,
+ * allowing the entire tree to be reconstructed. The tuples must be in depth first search order so that
+ * the tree is correctly reconstructed.
+ * @returns the corresponding taptree, or throws an exception if the tree cannot be reconstructed
+ */
+export function tapTreeFromList(leaves: TapLeaf[] = []): Taptree {
+ if (leaves.length === 1 && leaves[0].depth === 0)
+ return {
+ output: leaves[0].script,
+ version: leaves[0].leafVersion,
+ };
+
+ return instertLeavesInTree(leaves);
+}
+
+/**
+ * Checks the taproot input for signatures.
+ * @param input The PSBT input to check.
+ * @param action The action being performed.
+ * @returns True if the input has taproot signatures, false otherwise.
+ */
+export function checkTaprootInputForSigs(
+ input: PsbtInput,
+ action: string,
+): boolean {
+ const sigs = extractTaprootSigs(input);
+ return sigs.some(sig =>
+ signatureBlocksAction(sig, decodeSchnorrSignature, action),
+ );
+}
+
+/**
+ * Decodes a Schnorr signature.
+ * @param signature The signature to decode.
+ * @returns The decoded Schnorr signature.
+ */
+function decodeSchnorrSignature(signature: Uint8Array): {
+ signature: Uint8Array;
+ hashType: number;
+} {
+ return {
+ signature: signature.slice(0, 64),
+ hashType: signature.slice(64)[0] || Transaction.SIGHASH_DEFAULT,
+ };
+}
+
+/**
+ * Extracts taproot signatures from a PSBT input.
+ * @param input The PSBT input to extract signatures from.
+ * @returns An array of taproot signatures.
+ */
+function extractTaprootSigs(input: PsbtInput): Uint8Array[] {
+ const sigs: Uint8Array[] = [];
+ if (input.tapKeySig) sigs.push(input.tapKeySig);
+ if (input.tapScriptSig)
+ sigs.push(...input.tapScriptSig.map(s => s.signature));
+ if (!sigs.length) {
+ const finalTapKeySig = getTapKeySigFromWitness(input.finalScriptWitness);
+ if (finalTapKeySig) sigs.push(finalTapKeySig);
+ }
+
+ return sigs;
+}
+
+/**
+ * Gets the taproot signature from the witness.
+ * @param finalScriptWitness The final script witness.
+ * @returns The taproot signature, or undefined if not found.
+ */
+function getTapKeySigFromWitness(
+ finalScriptWitness?: Uint8Array,
+): Uint8Array | undefined {
+ if (!finalScriptWitness) return;
+ const witness = finalScriptWitness.slice(2);
+ // todo: add schnorr signature validation
+ if (witness.length === 64 || witness.length === 65) return witness;
+}
+
+/**
+ * Converts a binary tree to a BIP371 type list.
+ * @param tree The binary tap tree.
+ * @param leaves A list of tapleaves. Optional.
+ * @param depth The current depth. Optional.
+ * @returns A list of BIP 371 tapleaves.
+ * @throws Throws an error if the taptree cannot be converted to a tapleaf list.
+ */
+function _tapTreeToList(
+ tree: Taptree,
+ leaves: TapLeaf[] = [],
+ depth = 0,
+): TapLeaf[] {
+ if (depth > MAX_TAPTREE_DEPTH) throw new Error('Max taptree depth exceeded.');
+ if (!tree) return [];
+ if (isTapleaf(tree)) {
+ leaves.push({
+ depth,
+ leafVersion: tree.version || LEAF_VERSION_TAPSCRIPT,
+ script: tree.output,
+ });
+ return leaves;
+ }
+ if (tree[0]) _tapTreeToList(tree[0], leaves, depth + 1);
+ if (tree[1]) _tapTreeToList(tree[1], leaves, depth + 1);
+ return leaves;
+}
+
+// Just like Taptree, but it accepts empty branches
+type PartialTaptree =
+ | [PartialTaptree | Tapleaf, PartialTaptree | Tapleaf]
+ | Tapleaf
+ | undefined;
+
+/**
+ * Inserts the tapleaves into the taproot tree.
+ * @param leaves The tapleaves to insert.
+ * @returns The taproot tree.
+ * @throws Throws an error if there is no room left to insert a tapleaf in the tree.
+ */
+function instertLeavesInTree(leaves: TapLeaf[]): Taptree {
+ let tree: PartialTaptree;
+ for (const leaf of leaves) {
+ tree = instertLeafInTree(leaf, tree);
+ if (!tree) throw new Error(`No room left to insert tapleaf in tree`);
+ }
+
+ return tree as Taptree;
+}
+
+/**
+ * Inserts a tapleaf into the taproot tree.
+ * @param leaf The tapleaf to insert.
+ * @param tree The taproot tree.
+ * @param depth The current depth. Optional.
+ * @returns The updated taproot tree.
+ */
+function instertLeafInTree(
+ leaf: TapLeaf,
+ tree?: PartialTaptree,
+ depth = 0,
+): PartialTaptree {
+ if (depth > MAX_TAPTREE_DEPTH) throw new Error('Max taptree depth exceeded.');
+ if (leaf.depth === depth) {
+ if (!tree)
+ return {
+ output: leaf.script,
+ version: leaf.leafVersion,
+ };
+ return;
+ }
+
+ if (isTapleaf(tree)) return;
+ const leftSide = instertLeafInTree(leaf, tree && tree[0], depth + 1);
+ if (leftSide) return [leftSide, tree && tree[1]];
+
+ const rightSide = instertLeafInTree(leaf, tree && tree[1], depth + 1);
+ if (rightSide) return [tree && tree[0], rightSide];
+}
+
+/**
+ * Checks the input fields for mixed taproot and non-taproot fields.
+ * @param inputData The original input data.
+ * @param newInputData The new input data.
+ * @param action The action being performed.
+ * @throws Throws an error if the input fields are inconsistent.
+ */
+function checkMixedTaprootAndNonTaprootInputFields(
+ inputData: PsbtOutput,
+ newInputData: PsbtInput,
+ action: string,
+): void {
+ const isBadTaprootUpdate =
+ isTaprootInput(inputData) && hasNonTaprootFields(newInputData);
+ const isBadNonTaprootUpdate =
+ hasNonTaprootFields(inputData) && isTaprootInput(newInputData);
+ const hasMixedFields =
+ inputData === newInputData &&
+ isTaprootInput(newInputData) &&
+ hasNonTaprootFields(newInputData); // todo: bad? use !===
+
+ if (isBadTaprootUpdate || isBadNonTaprootUpdate || hasMixedFields)
+ throw new Error(
+ `Invalid arguments for Psbt.${action}. ` +
+ `Cannot use both taproot and non-taproot fields.`,
+ );
+}
+
+/**
+ * Checks the output fields for mixed taproot and non-taproot fields.
+ * @param inputData The original output data.
+ * @param newInputData The new output data.
+ * @param action The action being performed.
+ * @throws Throws an error if the output fields are inconsistent.
+ */
+function checkMixedTaprootAndNonTaprootOutputFields(
+ inputData: PsbtOutput,
+ newInputData: PsbtOutput,
+ action: string,
+): void {
+ const isBadTaprootUpdate =
+ isTaprootOutput(inputData) && hasNonTaprootFields(newInputData);
+ const isBadNonTaprootUpdate =
+ hasNonTaprootFields(inputData) && isTaprootOutput(newInputData);
+ const hasMixedFields =
+ inputData === newInputData &&
+ isTaprootOutput(newInputData) &&
+ hasNonTaprootFields(newInputData);
+
+ if (isBadTaprootUpdate || isBadNonTaprootUpdate || hasMixedFields)
+ throw new Error(
+ `Invalid arguments for Psbt.${action}. ` +
+ `Cannot use both taproot and non-taproot fields.`,
+ );
+}
+
+/**
+ * Checks if the tap leaf is part of the tap tree for the given input data.
+ * Throws an error if the tap leaf is not part of the tap tree.
+ * @param inputData - The original PsbtInput data.
+ * @param newInputData - The new PsbtInput data.
+ * @param action - The action being performed.
+ * @throws {Error} - If the tap leaf is not part of the tap tree.
+ */
+function checkIfTapLeafInTree(
+ inputData: PsbtInput,
+ newInputData: PsbtInput,
+ action: string,
+): void {
+ if (newInputData.tapMerkleRoot) {
+ const newLeafsInTree = (newInputData.tapLeafScript || []).every(l =>
+ isTapLeafInTree(l, newInputData.tapMerkleRoot),
+ );
+ const oldLeafsInTree = (inputData.tapLeafScript || []).every(l =>
+ isTapLeafInTree(l, newInputData.tapMerkleRoot),
+ );
+ if (!newLeafsInTree || !oldLeafsInTree)
+ throw new Error(
+ `Invalid arguments for Psbt.${action}. Tapleaf not part of taptree.`,
+ );
+ } else if (inputData.tapMerkleRoot) {
+ const newLeafsInTree = (newInputData.tapLeafScript || []).every(l =>
+ isTapLeafInTree(l, inputData.tapMerkleRoot),
+ );
+ if (!newLeafsInTree)
+ throw new Error(
+ `Invalid arguments for Psbt.${action}. Tapleaf not part of taptree.`,
+ );
+ }
+}
+
+/**
+ * Checks if a TapLeafScript is present in a Merkle tree.
+ * @param tapLeaf The TapLeafScript to check.
+ * @param merkleRoot The Merkle root of the tree. If not provided, the function assumes the TapLeafScript is present.
+ * @returns A boolean indicating whether the TapLeafScript is present in the tree.
+ */
+function isTapLeafInTree(
+ tapLeaf: TapLeafScript,
+ merkleRoot?: Uint8Array,
+): boolean {
+ if (!merkleRoot) return true;
+
+ const leafHash = tapleafHash({
+ output: tapLeaf.script,
+ version: tapLeaf.leafVersion,
+ });
+
+ const rootHash = rootHashFromPath(tapLeaf.controlBlock, leafHash);
+ return tools.compare(rootHash, merkleRoot) === 0;
+}
+
+/**
+ * Sorts the signatures in the input's tapScriptSig array based on their position in the tapLeaf script.
+ *
+ * @param input - The PsbtInput object.
+ * @param tapLeaf - The TapLeafScript object.
+ * @returns An array of sorted signatures as Buffers.
+ */
+function sortSignatures(
+ input: PsbtInput,
+ tapLeaf: TapLeafScript,
+): Uint8Array[] {
+ const leafHash = tapleafHash({
+ output: tapLeaf.script,
+ version: tapLeaf.leafVersion,
+ });
+
+ return (
+ (input.tapScriptSig || [])
+ // .filter(tss => tss.leafHash.equals(leafHash))
+ .filter(tss => tools.compare(tss.leafHash, leafHash) === 0)
+ .map(tss => addPubkeyPositionInScript(tapLeaf.script, tss))
+ .sort((t1, t2) => t2.positionInScript - t1.positionInScript)
+ .map(t => t.signature) as Uint8Array[]
+ );
+}
+
+/**
+ * Adds the position of a public key in a script to a TapScriptSig object.
+ * @param script The script in which to find the position of the public key.
+ * @param tss The TapScriptSig object to add the position to.
+ * @returns A TapScriptSigWitPosition object with the added position.
+ */
+function addPubkeyPositionInScript(
+ script: Uint8Array,
+ tss: TapScriptSig,
+): TapScriptSigWitPosition {
+ return Object.assign(
+ {
+ positionInScript: pubkeyPositionInScript(tss.pubkey, script),
+ },
+ tss,
+ ) as TapScriptSigWitPosition;
+}
+
+/**
+ * Find tapleaf by hash, or get the signed tapleaf with the shortest path.
+ */
+function findTapLeafToFinalize(
+ input: PsbtInput,
+ inputIndex: number,
+ leafHashToFinalize?: Uint8Array,
+): TapLeafScript {
+ if (!input.tapScriptSig || !input.tapScriptSig.length)
+ throw new Error(
+ `Can not finalize taproot input #${inputIndex}. No tapleaf script signature provided.`,
+ );
+ const tapLeaf = (input.tapLeafScript || [])
+ .sort((a, b) => a.controlBlock.length - b.controlBlock.length)
+ .find(leaf =>
+ canFinalizeLeaf(leaf, input.tapScriptSig!, leafHashToFinalize),
+ );
+
+ if (!tapLeaf)
+ throw new Error(
+ `Can not finalize taproot input #${inputIndex}. Signature for tapleaf script not found.`,
+ );
+
+ return tapLeaf;
+}
+
+/**
+ * Determines whether a TapLeafScript can be finalized.
+ *
+ * @param leaf - The TapLeafScript to check.
+ * @param tapScriptSig - The array of TapScriptSig objects.
+ * @param hash - The optional hash to compare with the leaf hash.
+ * @returns A boolean indicating whether the TapLeafScript can be finalized.
+ */
+function canFinalizeLeaf(
+ leaf: TapLeafScript,
+ tapScriptSig: TapScriptSig[],
+ hash?: Uint8Array,
+): boolean {
+ const leafHash = tapleafHash({
+ output: leaf.script,
+ version: leaf.leafVersion,
+ });
+ const whiteListedHash = !hash || tools.compare(leafHash, hash) === 0;
+ return (
+ whiteListedHash &&
+ tapScriptSig!.find(tss => tools.compare(tss.leafHash, leafHash) === 0) !==
+ undefined
+ );
+}
+
+/**
+ * Checks if the given PsbtInput or PsbtOutput has non-taproot fields.
+ * Non-taproot fields include redeemScript, witnessScript, and bip32Derivation.
+ * @param io The PsbtInput or PsbtOutput to check.
+ * @returns A boolean indicating whether the given input or output has non-taproot fields.
+ */
+function hasNonTaprootFields(io: PsbtInput | PsbtOutput): boolean {
+ return (
+ io &&
+ !!(
+ io.redeemScript ||
+ io.witnessScript ||
+ (io.bip32Derivation && io.bip32Derivation.length)
+ )
+ );
+}
+
+interface TapScriptSigWitPosition extends TapScriptSig {
+ positionInScript: number;
+}
diff --git a/ts_src/psbt/psbtutils.ts b/ts_src/psbt/psbtutils.ts
new file mode 100644
index 000000000..298f4769c
--- /dev/null
+++ b/ts_src/psbt/psbtutils.ts
@@ -0,0 +1,202 @@
+import * as varuint from 'varuint-bitcoin';
+import { PartialSig, PsbtInput } from 'bip174';
+import * as bscript from '../script.js';
+import { Transaction } from '../transaction.js';
+import { hash160 } from '../crypto.js';
+import * as payments from '../payments/index.js';
+import * as tools from 'uint8array-tools';
+
+/**
+ * Checks if a given payment factory can generate a payment script from a given script.
+ * @param payment The payment factory to check.
+ * @returns A function that takes a script and returns a boolean indicating whether the payment factory can generate a payment script from the script.
+ */
+function isPaymentFactory(payment: any): (script: Uint8Array) => boolean {
+ return (script: Uint8Array): boolean => {
+ try {
+ payment({ output: script });
+ return true;
+ } catch (err) {
+ return false;
+ }
+ };
+}
+
+export const isP2MS = isPaymentFactory(payments.p2ms);
+export const isP2PK = isPaymentFactory(payments.p2pk);
+export const isP2PKH = isPaymentFactory(payments.p2pkh);
+export const isP2WPKH = isPaymentFactory(payments.p2wpkh);
+export const isP2WSHScript = isPaymentFactory(payments.p2wsh);
+export const isP2SHScript = isPaymentFactory(payments.p2sh);
+export const isP2TR = isPaymentFactory(payments.p2tr);
+
+/**
+ * Converts a witness stack to a script witness.
+ * @param witness The witness stack to convert.
+ * @returns The script witness as a Buffer.
+ */
+export function witnessStackToScriptWitness(witness: Uint8Array[]): Uint8Array {
+ let buffer = new Uint8Array(0);
+
+ function writeSlice(slice: Uint8Array): void {
+ buffer = tools.concat([buffer, slice]);
+ }
+
+ function writeVarInt(i: number): void {
+ const currentLen = buffer.length;
+ const varintLen = varuint.encodingLength(i);
+
+ buffer = tools.concat([buffer, new Uint8Array(varintLen)]);
+ varuint.encode(i, buffer, currentLen);
+ }
+
+ function writeVarSlice(slice: Uint8Array): void {
+ writeVarInt(slice.length);
+ writeSlice(slice);
+ }
+
+ function writeVector(vector: Uint8Array[]): void {
+ writeVarInt(vector.length);
+ vector.forEach(writeVarSlice);
+ }
+
+ writeVector(witness);
+
+ return buffer;
+}
+
+/**
+ * Finds the position of a public key in a script.
+ * @param pubkey The public key to search for.
+ * @param script The script to search in.
+ * @returns The index of the public key in the script, or -1 if not found.
+ * @throws {Error} If there is an unknown script error.
+ */
+export function pubkeyPositionInScript(
+ pubkey: Uint8Array,
+ script: Uint8Array,
+): number {
+ const pubkeyHash = hash160(pubkey);
+ const pubkeyXOnly = pubkey.slice(1, 33); // slice before calling?
+
+ const decompiled = bscript.decompile(script);
+ if (decompiled === null) throw new Error('Unknown script error');
+
+ return decompiled.findIndex(element => {
+ if (typeof element === 'number') return false;
+ return (
+ tools.compare(pubkey, element) === 0 ||
+ tools.compare(pubkeyHash, element) === 0 ||
+ tools.compare(pubkeyXOnly, element) === 0
+ );
+ });
+}
+
+/**
+ * Checks if a public key is present in a script.
+ * @param pubkey The public key to check.
+ * @param script The script to search in.
+ * @returns A boolean indicating whether the public key is present in the script.
+ */
+export function pubkeyInScript(
+ pubkey: Uint8Array,
+ script: Uint8Array,
+): boolean {
+ return pubkeyPositionInScript(pubkey, script) !== -1;
+}
+
+/**
+ * Checks if an input contains a signature for a specific action.
+ * @param input - The input to check.
+ * @param action - The action to check for.
+ * @returns A boolean indicating whether the input contains a signature for the specified action.
+ */
+export function checkInputForSig(input: PsbtInput, action: string): boolean {
+ const pSigs = extractPartialSigs(input);
+ return pSigs.some(pSig =>
+ signatureBlocksAction(pSig, bscript.signature.decode, action),
+ );
+}
+
+type SignatureDecodeFunc = (buffer: Uint8Array) => {
+ signature: Uint8Array;
+ hashType: number;
+};
+
+/**
+ * Determines if a given action is allowed for a signature block.
+ * @param signature - The signature block.
+ * @param signatureDecodeFn - The function used to decode the signature.
+ * @param action - The action to be checked.
+ * @returns True if the action is allowed, false otherwise.
+ */
+export function signatureBlocksAction(
+ signature: Uint8Array,
+ signatureDecodeFn: SignatureDecodeFunc,
+ action: string,
+): boolean {
+ const { hashType } = signatureDecodeFn(signature);
+ const whitelist: string[] = [];
+ const isAnyoneCanPay = hashType & Transaction.SIGHASH_ANYONECANPAY;
+ if (isAnyoneCanPay) whitelist.push('addInput');
+ const hashMod = hashType & 0x1f;
+ switch (hashMod) {
+ case Transaction.SIGHASH_ALL:
+ break;
+ case Transaction.SIGHASH_SINGLE:
+ case Transaction.SIGHASH_NONE:
+ whitelist.push('addOutput');
+ whitelist.push('setInputSequence');
+ break;
+ }
+ if (whitelist.indexOf(action) === -1) {
+ return true;
+ }
+ return false;
+}
+
+/**
+ * Extracts the signatures from a PsbtInput object.
+ * If the input has partial signatures, it returns an array of the signatures.
+ * If the input does not have partial signatures, it checks if it has a finalScriptSig or finalScriptWitness.
+ * If it does, it extracts the signatures from the final scripts and returns them.
+ * If none of the above conditions are met, it returns an empty array.
+ *
+ * @param input - The PsbtInput object from which to extract the signatures.
+ * @returns An array of signatures extracted from the PsbtInput object.
+ */
+function extractPartialSigs(input: PsbtInput): Uint8Array[] {
+ let pSigs: PartialSig[] = [];
+ if ((input.partialSig || []).length === 0) {
+ if (!input.finalScriptSig && !input.finalScriptWitness) return [];
+ pSigs = getPsigsFromInputFinalScripts(input);
+ } else {
+ pSigs = input.partialSig!;
+ }
+ return pSigs.map(p => p.signature);
+}
+
+/**
+ * Retrieves the partial signatures (Psigs) from the input's final scripts.
+ * Psigs are extracted from both the final scriptSig and final scriptWitness of the input.
+ * Only canonical script signatures are considered.
+ *
+ * @param input - The PsbtInput object representing the input.
+ * @returns An array of PartialSig objects containing the extracted Psigs.
+ */
+function getPsigsFromInputFinalScripts(input: PsbtInput): PartialSig[] {
+ const scriptItems = !input.finalScriptSig
+ ? []
+ : bscript.decompile(input.finalScriptSig) || [];
+ const witnessItems = !input.finalScriptWitness
+ ? []
+ : bscript.decompile(input.finalScriptWitness) || [];
+ return scriptItems
+ .concat(witnessItems)
+ .filter(item => {
+ return (
+ item instanceof Uint8Array && bscript.isCanonicalScriptSignature(item)
+ );
+ })
+ .map(sig => ({ signature: sig })) as PartialSig[];
+}
diff --git a/ts_src/push_data.ts b/ts_src/push_data.ts
index 56bb02ab2..4c05d2038 100644
--- a/ts_src/push_data.ts
+++ b/ts_src/push_data.ts
@@ -1,44 +1,68 @@
-import { OPS } from './ops';
+import { OPS } from './ops.js';
+import * as tools from 'uint8array-tools';
+/**
+ * Calculates the encoding length of a number used for push data in Bitcoin transactions.
+ * @param i The number to calculate the encoding length for.
+ * @returns The encoding length of the number.
+ */
export function encodingLength(i: number): number {
return i < OPS.OP_PUSHDATA1 ? 1 : i <= 0xff ? 2 : i <= 0xffff ? 3 : 5;
}
-export function encode(buffer: Buffer, num: number, offset: number): number {
+/**
+ * Encodes a number into a buffer using a variable-length encoding scheme.
+ * The encoded buffer is written starting at the specified offset.
+ * Returns the size of the encoded buffer.
+ *
+ * @param buffer - The buffer to write the encoded data into.
+ * @param num - The number to encode.
+ * @param offset - The offset at which to start writing the encoded buffer.
+ * @returns The size of the encoded buffer.
+ */
+export function encode(
+ buffer: Uint8Array,
+ num: number,
+ offset: number,
+): number {
const size = encodingLength(num);
// ~6 bit
if (size === 1) {
- buffer.writeUInt8(num, offset);
-
+ tools.writeUInt8(buffer, offset, num);
// 8 bit
} else if (size === 2) {
- buffer.writeUInt8(OPS.OP_PUSHDATA1, offset);
- buffer.writeUInt8(num, offset + 1);
+ tools.writeUInt8(buffer, offset, OPS.OP_PUSHDATA1);
+ tools.writeUInt8(buffer, offset + 1, num);
// 16 bit
} else if (size === 3) {
- buffer.writeUInt8(OPS.OP_PUSHDATA2, offset);
- buffer.writeUInt16LE(num, offset + 1);
-
+ tools.writeUInt8(buffer, offset, OPS.OP_PUSHDATA2);
+ tools.writeUInt16(buffer, offset + 1, num, 'LE');
// 32 bit
} else {
- buffer.writeUInt8(OPS.OP_PUSHDATA4, offset);
- buffer.writeUInt32LE(num, offset + 1);
+ tools.writeUInt8(buffer, offset, OPS.OP_PUSHDATA4);
+ tools.writeUInt32(buffer, offset + 1, num, 'LE');
}
return size;
}
+/**
+ * Decodes a buffer and returns information about the opcode, number, and size.
+ * @param buffer - The buffer to decode.
+ * @param offset - The offset within the buffer to start decoding.
+ * @returns An object containing the opcode, number, and size, or null if decoding fails.
+ */
export function decode(
- buffer: Buffer,
+ buffer: Uint8Array,
offset: number,
): {
opcode: number;
number: number;
size: number;
} | null {
- const opcode = buffer.readUInt8(offset);
+ const opcode = tools.readUInt8(buffer, offset);
let num: number;
let size: number;
@@ -50,13 +74,13 @@ export function decode(
// 8 bit
} else if (opcode === OPS.OP_PUSHDATA1) {
if (offset + 2 > buffer.length) return null;
- num = buffer.readUInt8(offset + 1);
+ num = tools.readUInt8(buffer, offset + 1);
size = 2;
// 16 bit
} else if (opcode === OPS.OP_PUSHDATA2) {
if (offset + 3 > buffer.length) return null;
- num = buffer.readUInt16LE(offset + 1);
+ num = tools.readUInt16(buffer, offset + 1, 'LE');
size = 3;
// 32 bit
@@ -64,7 +88,7 @@ export function decode(
if (offset + 5 > buffer.length) return null;
if (opcode !== OPS.OP_PUSHDATA4) throw new Error('Unexpected opcode');
- num = buffer.readUInt32LE(offset + 1);
+ num = tools.readUInt32(buffer, offset + 1, 'LE');
size = 5;
}
diff --git a/ts_src/script.ts b/ts_src/script.ts
index 5d20ebc01..6e6f8580c 100644
--- a/ts_src/script.ts
+++ b/ts_src/script.ts
@@ -1,59 +1,132 @@
-import * as bip66 from './bip66';
-import { OPS, REVERSE_OPS } from './ops';
-import { Stack } from './payments';
-import * as pushdata from './push_data';
-import * as scriptNumber from './script_number';
-import * as scriptSignature from './script_signature';
-import * as types from './types';
-const { typeforce } = types;
-
+/**
+ * Script tools module for working with Bitcoin scripts.
+ * Provides utilities such as decompiling, compiling, converting to/from ASM, stack manipulation,
+ * and script validation functions.
+ *
+ * @packageDocumentation
+ */
+
+import * as bip66 from './bip66.js';
+import { OPS } from './ops.js';
+import { Stack } from './payments/index.js';
+import * as pushdata from './push_data.js';
+import * as scriptNumber from './script_number.js';
+import * as scriptSignature from './script_signature.js';
+import * as types from './types.js';
+import * as tools from 'uint8array-tools';
+import * as v from 'valibot';
+
+/** Base opcode for OP_INT values. */
const OP_INT_BASE = OPS.OP_RESERVED; // OP_1 - 1
export { OPS };
+/** Validation schema for a Bitcoin script stack. */
+const StackSchema = v.array(v.union([v.instance(Uint8Array), v.number()]));
+
+/**
+ * Determines if a value corresponds to an OP_INT opcode.
+ *
+ * @param value - The opcode to check.
+ * @returns True if the value is an OP_INT, false otherwise.
+ */
function isOPInt(value: number): boolean {
return (
- types.Number(value) &&
+ v.is(v.number(), value) &&
(value === OPS.OP_0 ||
(value >= OPS.OP_1 && value <= OPS.OP_16) ||
value === OPS.OP_1NEGATE)
);
}
-function isPushOnlyChunk(value: number | Buffer): boolean {
- return types.Buffer(value) || isOPInt(value as number);
+/**
+ * Checks if a script chunk is push-only (contains only data or OP_INT opcodes).
+ *
+ * @param value - The chunk to check.
+ * @returns True if the chunk is push-only, false otherwise.
+ */
+function isPushOnlyChunk(value: number | Uint8Array): boolean {
+ return v.is(types.BufferSchema, value) || isOPInt(value as number);
}
+/**
+ * Determines if a stack consists of only push operations.
+ *
+ * @param value - The stack to check.
+ * @returns True if all elements in the stack are push-only, false otherwise.
+ */
export function isPushOnly(value: Stack): boolean {
- return types.Array(value) && value.every(isPushOnlyChunk);
+ return v.is(
+ v.pipe(v.any(), v.everyItem(isPushOnlyChunk as (x: any) => boolean)),
+ value,
+ );
}
-function asMinimalOP(buffer: Buffer): number | void {
+/**
+ * Counts the number of non-push-only opcodes in a stack.
+ *
+ * @param value - The stack to analyze.
+ * @returns The count of non-push-only opcodes.
+ */
+export function countNonPushOnlyOPs(value: Stack): number {
+ return value.length - value.filter(isPushOnlyChunk).length;
+}
+
+/**
+ * Converts a minimal script buffer to its corresponding opcode, if applicable.
+ *
+ * @param buffer - The buffer to check.
+ * @returns The corresponding opcode or undefined if not minimal.
+ */
+function asMinimalOP(buffer: Uint8Array): number | void {
if (buffer.length === 0) return OPS.OP_0;
if (buffer.length !== 1) return;
if (buffer[0] >= 1 && buffer[0] <= 16) return OP_INT_BASE + buffer[0];
if (buffer[0] === 0x81) return OPS.OP_1NEGATE;
}
-function chunksIsBuffer(buf: Buffer | Stack): buf is Buffer {
- return Buffer.isBuffer(buf);
+/**
+ * Determines if a buffer or stack is a Uint8Array.
+ *
+ * @param buf - The buffer or stack to check.
+ * @returns True if the input is a Uint8Array, false otherwise.
+ */
+function chunksIsBuffer(buf: Uint8Array | Stack): buf is Uint8Array {
+ return buf instanceof Uint8Array;
}
-function chunksIsArray(buf: Buffer | Stack): buf is Stack {
- return types.Array(buf);
+/**
+ * Determines if a buffer or stack is a valid stack.
+ *
+ * @param buf - The buffer or stack to check.
+ * @returns True if the input is a stack, false otherwise.
+ */
+function chunksIsArray(buf: Uint8Array | Stack): buf is Stack {
+ return v.is(StackSchema, buf);
}
-function singleChunkIsBuffer(buf: number | Buffer): buf is Buffer {
- return Buffer.isBuffer(buf);
+/**
+ * Determines if a single chunk is a Uint8Array.
+ *
+ * @param buf - The chunk to check.
+ * @returns True if the chunk is a Uint8Array, false otherwise.
+ */
+function singleChunkIsBuffer(buf: number | Uint8Array): buf is Uint8Array {
+ return buf instanceof Uint8Array;
}
-export function compile(chunks: Buffer | Stack): Buffer {
- // TODO: remove me
+/**
+ * Compiles an array of script chunks into a Uint8Array.
+ *
+ * @param chunks - The chunks to compile.
+ * @returns The compiled script as a Uint8Array.
+ * @throws Error if compilation fails.
+ */
+export function compile(chunks: Uint8Array | Stack): Uint8Array {
if (chunksIsBuffer(chunks)) return chunks;
- typeforce(types.Array, chunks);
+ v.parse(StackSchema, chunks);
const bufferSize = chunks.reduce((accum: number, chunk) => {
- // data chunk
if (singleChunkIsBuffer(chunk)) {
// adhere to BIP62.3, minimal push policy
if (chunk.length === 1 && asMinimalOP(chunk) !== undefined) {
@@ -63,31 +136,29 @@ export function compile(chunks: Buffer | Stack): Buffer {
return accum + pushdata.encodingLength(chunk.length) + chunk.length;
}
- // opcode
return accum + 1;
- }, 0.0);
+ }, 0);
- const buffer = Buffer.allocUnsafe(bufferSize);
+ const buffer = new Uint8Array(bufferSize);
let offset = 0;
chunks.forEach(chunk => {
- // data chunk
if (singleChunkIsBuffer(chunk)) {
// adhere to BIP62.3, minimal push policy
const opcode = asMinimalOP(chunk);
if (opcode !== undefined) {
- buffer.writeUInt8(opcode, offset);
+ tools.writeUInt8(buffer, offset, opcode);
offset += 1;
return;
}
offset += pushdata.encode(buffer, chunk.length, offset);
- chunk.copy(buffer, offset);
+ buffer.set(chunk, offset);
offset += chunk.length;
// opcode
} else {
- buffer.writeUInt8(chunk, offset);
+ tools.writeUInt8(buffer, offset, chunk);
offset += 1;
}
});
@@ -96,21 +167,25 @@ export function compile(chunks: Buffer | Stack): Buffer {
return buffer;
}
+/**
+ * Decompiles a script buffer into an array of chunks.
+ *
+ * @param buffer - The script buffer to decompile.
+ * @returns The decompiled chunks or null if decompilation fails.
+ */
export function decompile(
- buffer: Buffer | Array,
-): Array | null {
- // TODO: remove me
+ buffer: Uint8Array | Array,
+): Array | null {
if (chunksIsArray(buffer)) return buffer;
- typeforce(types.Buffer, buffer);
+ v.parse(types.BufferSchema, buffer);
- const chunks: Array = [];
+ const chunks: Array = [];
let i = 0;
while (i < buffer.length) {
const opcode = buffer[i];
- // data chunk
if (opcode > OPS.OP_0 && opcode <= OPS.OP_PUSHDATA4) {
const d = pushdata.decode(buffer, i);
@@ -131,8 +206,6 @@ export function decompile(
} else {
chunks.push(data);
}
-
- // opcode
} else {
chunks.push(opcode);
@@ -143,71 +216,117 @@ export function decompile(
return chunks;
}
-export function toASM(chunks: Buffer | Array): string {
+/**
+ * Converts the given chunks into an ASM (Assembly) string representation.
+ * If the chunks parameter is a Buffer, it will be decompiled into a Stack before conversion.
+ * @param chunks - The chunks to convert into ASM.
+ * @returns The ASM string representation of the chunks.
+ */
+export function toASM(chunks: Uint8Array | Array): string {
if (chunksIsBuffer(chunks)) {
chunks = decompile(chunks) as Stack;
}
-
- return chunks
+ if (!chunks) {
+ throw new Error('Could not convert invalid chunks to ASM');
+ }
+ return (chunks as Stack)
.map(chunk => {
- // data?
if (singleChunkIsBuffer(chunk)) {
const op = asMinimalOP(chunk);
- if (op === undefined) return chunk.toString('hex');
+ if (op === undefined) return tools.toHex(chunk);
chunk = op as number;
}
// opcode!
- return REVERSE_OPS[chunk];
+ return OPS[chunk];
})
.join(' ');
}
-export function fromASM(asm: string): Buffer {
- typeforce(types.String, asm);
+/**
+ * Converts an ASM string to a Buffer.
+ * @param asm The ASM string to convert.
+ * @returns The converted Buffer.
+ */
+export function fromASM(asm: string): Uint8Array {
+ v.parse(v.string(), asm);
+ // Compile the ASM string into a Uint8Array
return compile(
- asm.split(' ').map(chunkStr => {
- // opcode?
- if (OPS[chunkStr] !== undefined) return OPS[chunkStr];
- typeforce(types.Hex, chunkStr);
+ asm.split(' ').map((chunk: string): number | Uint8Array => {
+ // Check if the chunk is an opcode
+ if (isNaN(Number(chunk)) && chunk in OPS) {
+ return OPS[chunk as keyof typeof OPS];
+ }
+
+ // Validate if the chunk is a hexadecimal string
+ v.parse(types.HexSchema, chunk);
- // data!
- return Buffer.from(chunkStr, 'hex');
+ // Convert the chunk to Uint8Array data
+ return tools.fromHex(chunk);
}),
);
}
-export function toStack(chunks: Buffer | Array): Buffer[] {
+/**
+ * Converts the given chunks into a stack of buffers.
+ *
+ * @param chunks - The chunks to convert.
+ * @returns The stack of buffers.
+ */
+export function toStack(
+ chunks: Uint8Array | Array,
+): Uint8Array[] {
chunks = decompile(chunks) as Stack;
- typeforce(isPushOnly, chunks);
+ v.parse(v.custom(isPushOnly as (x: any) => boolean), chunks);
return chunks.map(op => {
if (singleChunkIsBuffer(op)) return op;
- if (op === OPS.OP_0) return Buffer.allocUnsafe(0);
+ if (op === OPS.OP_0) return new Uint8Array(0);
return scriptNumber.encode(op - OP_INT_BASE);
});
}
-export function isCanonicalPubKey(buffer: Buffer): boolean {
+/**
+ * Checks if the provided buffer is a canonical public key.
+ *
+ * @param buffer - The buffer to check, expected to be a Uint8Array.
+ * @returns A boolean indicating whether the buffer is a canonical public key.
+ */
+export function isCanonicalPubKey(buffer: Uint8Array): boolean {
return types.isPoint(buffer);
}
+/**
+ * Checks if the provided hash type is defined.
+ *
+ * A hash type is considered defined if its modified value (after masking with ~0x80)
+ * is greater than 0x00 and less than 0x04.
+ *
+ * @param hashType - The hash type to check.
+ * @returns True if the hash type is defined, false otherwise.
+ */
export function isDefinedHashType(hashType: number): boolean {
const hashTypeMod = hashType & ~0x80;
- // return hashTypeMod > SIGHASH_ALL && hashTypeMod < SIGHASH_SINGLE
return hashTypeMod > 0x00 && hashTypeMod < 0x04;
}
-export function isCanonicalScriptSignature(buffer: Buffer): boolean {
- if (!Buffer.isBuffer(buffer)) return false;
+/**
+ * Checks if the provided buffer is a canonical script signature.
+ *
+ * A canonical script signature is a valid DER-encoded signature followed by a valid hash type byte.
+ *
+ * @param buffer - The buffer to check.
+ * @returns `true` if the buffer is a canonical script signature, `false` otherwise.
+ */
+export function isCanonicalScriptSignature(buffer: Uint8Array): boolean {
+ if (!(buffer instanceof Uint8Array)) return false;
if (!isDefinedHashType(buffer[buffer.length - 1])) return false;
return bip66.check(buffer.slice(0, -1));
}
-// tslint:disable-next-line variable-name
export const number = scriptNumber;
export const signature = scriptSignature;
diff --git a/ts_src/script_number.ts b/ts_src/script_number.ts
index a4c502fc9..7e4c8bbcb 100644
--- a/ts_src/script_number.ts
+++ b/ts_src/script_number.ts
@@ -1,5 +1,17 @@
+import * as tools from 'uint8array-tools';
+
+/**
+ * Decodes a script number from a buffer.
+ *
+ * @param buffer - The buffer containing the script number.
+ * @param maxLength - The maximum length of the script number. Defaults to 4.
+ * @param minimal - Whether the script number should be minimal. Defaults to true.
+ * @returns The decoded script number.
+ * @throws {TypeError} If the script number overflows the maximum length.
+ * @throws {Error} If the script number is not minimally encoded when minimal is true.
+ */
export function decode(
- buffer: Buffer,
+ buffer: Uint8Array,
maxLength?: number,
minimal?: boolean,
): number {
@@ -18,8 +30,8 @@ export function decode(
// 40-bit
if (length === 5) {
- const a = buffer.readUInt32LE(0);
- const b = buffer.readUInt8(4);
+ const a = tools.readUInt32(buffer, 0, 'LE');
+ const b = tools.readUInt8(buffer, 4);
if (b & 0x80) return -((b & ~0x80) * 0x100000000 + a);
return b * 0x100000000 + a;
@@ -40,29 +52,35 @@ function scriptNumSize(i: number): number {
return i > 0x7fffffff
? 5
: i > 0x7fffff
- ? 4
- : i > 0x7fff
- ? 3
- : i > 0x7f
- ? 2
- : i > 0x00
- ? 1
- : 0;
+ ? 4
+ : i > 0x7fff
+ ? 3
+ : i > 0x7f
+ ? 2
+ : i > 0x00
+ ? 1
+ : 0;
}
-export function encode(_number: number): Buffer {
+/**
+ * Encodes a number into a Uint8Array using a specific format.
+ *
+ * @param _number - The number to encode.
+ * @returns The encoded number as a Uint8Array.
+ */
+export function encode(_number: number): Uint8Array {
let value = Math.abs(_number);
const size = scriptNumSize(value);
- const buffer = Buffer.allocUnsafe(size);
+ const buffer = new Uint8Array(size);
const negative = _number < 0;
for (let i = 0; i < size; ++i) {
- buffer.writeUInt8(value & 0xff, i);
+ tools.writeUInt8(buffer, i, value & 0xff);
value >>= 8;
}
if (buffer[size - 1] & 0x80) {
- buffer.writeUInt8(negative ? 0x80 : 0x00, size - 1);
+ tools.writeUInt8(buffer, size - 1, negative ? 0x80 : 0x00);
} else if (negative) {
buffer[size - 1] |= 0x80;
}
diff --git a/ts_src/script_signature.ts b/ts_src/script_signature.ts
index df206e813..ba536b532 100644
--- a/ts_src/script_signature.ts
+++ b/ts_src/script_signature.ts
@@ -1,63 +1,90 @@
-import * as bip66 from './bip66';
-import * as types from './types';
-const { typeforce } = types;
+import * as bip66 from './bip66.js';
+import { isDefinedHashType } from './script.js';
+import * as v from 'valibot';
+import * as tools from 'uint8array-tools';
+import { NBufferSchemaFactory, UInt8Schema } from './types.js';
-const ZERO = Buffer.alloc(1, 0);
-function toDER(x: Buffer): Buffer {
+const ZERO = new Uint8Array(1);
+/**
+ * Converts a buffer to a DER-encoded buffer.
+ * @param x - The buffer to be converted.
+ * @returns The DER-encoded buffer.
+ */
+function toDER(x: Uint8Array): Uint8Array {
let i = 0;
while (x[i] === 0) ++i;
if (i === x.length) return ZERO;
x = x.slice(i);
- if (x[0] & 0x80) return Buffer.concat([ZERO, x], 1 + x.length);
+ if (x[0] & 0x80) return tools.concat([ZERO, x]);
return x;
}
-function fromDER(x: Buffer): Buffer {
+/**
+ * Converts a DER-encoded signature to a buffer.
+ * If the first byte of the input buffer is 0x00, it is skipped.
+ * The resulting buffer is 32 bytes long, filled with zeros if necessary.
+ * @param x - The DER-encoded signature.
+ * @returns The converted buffer.
+ */
+function fromDER(x: Uint8Array): Uint8Array {
if (x[0] === 0x00) x = x.slice(1);
- const buffer = Buffer.alloc(32, 0);
+ const buffer = new Uint8Array(32);
const bstart = Math.max(0, 32 - x.length);
- x.copy(buffer, bstart);
+ buffer.set(x, bstart);
return buffer;
}
interface ScriptSignature {
- signature: Buffer;
+ signature: Uint8Array;
hashType: number;
}
// BIP62: 1 byte hashType flag (only 0x01, 0x02, 0x03, 0x81, 0x82 and 0x83 are allowed)
-export function decode(buffer: Buffer): ScriptSignature {
- const hashType = buffer.readUInt8(buffer.length - 1);
- const hashTypeMod = hashType & ~0x80;
- if (hashTypeMod <= 0 || hashTypeMod >= 4)
+/**
+ * Decodes a buffer into a ScriptSignature object.
+ * @param buffer - The buffer to decode.
+ * @returns The decoded ScriptSignature object.
+ * @throws Error if the hashType is invalid.
+ */
+export function decode(buffer: Uint8Array): ScriptSignature {
+ const hashType = tools.readUInt8(buffer, buffer.length - 1);
+ if (!isDefinedHashType(hashType)) {
throw new Error('Invalid hashType ' + hashType);
+ }
- const decoded = bip66.decode(buffer.slice(0, -1));
+ const decoded = bip66.decode(buffer.subarray(0, -1));
const r = fromDER(decoded.r);
const s = fromDER(decoded.s);
- const signature = Buffer.concat([r, s], 64);
+ const signature = tools.concat([r, s]);
return { signature, hashType };
}
-export function encode(signature: Buffer, hashType: number): Buffer {
- typeforce(
- {
- signature: types.BufferN(64),
- hashType: types.UInt8,
- },
+/**
+ * Encodes a signature and hash type into a buffer.
+ * @param signature - The signature to encode.
+ * @param hashType - The hash type to encode.
+ * @returns The encoded buffer.
+ * @throws Error if the hashType is invalid.
+ */
+export function encode(signature: Uint8Array, hashType: number): Uint8Array {
+ v.parse(
+ v.object({
+ signature: NBufferSchemaFactory(64),
+ hashType: UInt8Schema,
+ }),
{ signature, hashType },
);
- const hashTypeMod = hashType & ~0x80;
- if (hashTypeMod <= 0 || hashTypeMod >= 4)
+ if (!isDefinedHashType(hashType)) {
throw new Error('Invalid hashType ' + hashType);
+ }
- const hashTypeBuffer = Buffer.allocUnsafe(1);
- hashTypeBuffer.writeUInt8(hashType, 0);
+ const hashTypeBuffer = new Uint8Array(1);
+ tools.writeUInt8(hashTypeBuffer, 0, hashType);
const r = toDER(signature.slice(0, 32));
const s = toDER(signature.slice(32, 64));
- return Buffer.concat([bip66.encode(r, s), hashTypeBuffer]);
+ return tools.concat([bip66.encode(r, s), hashTypeBuffer]);
}
diff --git a/ts_src/transaction.ts b/ts_src/transaction.ts
index 416f20efe..9dce283e4 100644
--- a/ts_src/transaction.ts
+++ b/ts_src/transaction.ts
@@ -3,20 +3,22 @@ import {
BufferWriter,
reverseBuffer,
varuint,
-} from './bufferutils';
-import * as bcrypto from './crypto';
-import * as bscript from './script';
-import { OPS as opcodes } from './script';
-import * as types from './types';
-const { typeforce } = types;
-
-function varSliceSize(someScript: Buffer): number {
+} from './bufferutils.js';
+import * as bcrypto from './crypto.js';
+import { sha256 } from '@noble/hashes/sha256';
+import * as bscript from './script.js';
+import { OPS as opcodes } from './script.js';
+import * as types from './types.js';
+import * as tools from 'uint8array-tools';
+import * as v from 'valibot';
+
+function varSliceSize(someScript: Uint8Array): number {
const length = someScript.length;
return varuint.encodingLength(length) + length;
}
-function vectorSize(someVector: Buffer[]): number {
+function vectorSize(someVector: Uint8Array[]): number {
const length = someVector.length;
return (
@@ -27,17 +29,15 @@ function vectorSize(someVector: Buffer[]): number {
);
}
-const EMPTY_BUFFER: Buffer = Buffer.allocUnsafe(0);
-const EMPTY_WITNESS: Buffer[] = [];
-const ZERO: Buffer = Buffer.from(
+const EMPTY_BUFFER = new Uint8Array(0);
+const EMPTY_WITNESS: Uint8Array[] = [];
+const ZERO = tools.fromHex(
'0000000000000000000000000000000000000000000000000000000000000000',
- 'hex',
);
-const ONE: Buffer = Buffer.from(
+const ONE = tools.fromHex(
'0000000000000000000000000000000000000000000000000000000000000001',
- 'hex',
);
-const VALUE_UINT64_MAX: Buffer = Buffer.from('ffffffffffffffff', 'hex');
+const VALUE_UINT64_MAX = tools.fromHex('ffffffffffffffff');
const BLANK_OUTPUT = {
script: EMPTY_BUFFER,
valueBuffer: VALUE_UINT64_MAX,
@@ -48,18 +48,21 @@ function isOutput(out: Output): boolean {
}
export interface Output {
- script: Buffer;
- value: number;
+ script: Uint8Array;
+ value: bigint;
}
export interface Input {
- hash: Buffer;
+ hash: Uint8Array;
index: number;
- script: Buffer;
+ script: Uint8Array;
sequence: number;
- witness: Buffer[];
+ witness: Uint8Array[];
}
+/**
+ * Represents a Bitcoin transaction.
+ */
export class Transaction {
static readonly DEFAULT_SEQUENCE = 0xffffffff;
static readonly SIGHASH_DEFAULT = 0x00;
@@ -71,22 +74,30 @@ export class Transaction {
static readonly SIGHASH_INPUT_MASK = 0x80;
static readonly ADVANCED_TRANSACTION_MARKER = 0x00;
static readonly ADVANCED_TRANSACTION_FLAG = 0x01;
+ static readonly MWEB_PEGOUT_FLAG = 0x08;
- static fromBuffer(buffer: Buffer, _NO_STRICT?: boolean): Transaction {
+ static fromBuffer(buffer: Uint8Array, _NO_STRICT?: boolean): Transaction {
const bufferReader = new BufferReader(buffer);
const tx = new Transaction();
- tx.version = bufferReader.readInt32();
+ tx.version = bufferReader.readUInt32();
const marker = bufferReader.readUInt8();
const flag = bufferReader.readUInt8();
let hasWitnesses = false;
+ let hasMweb = false;
+
if (
marker === Transaction.ADVANCED_TRANSACTION_MARKER &&
flag === Transaction.ADVANCED_TRANSACTION_FLAG
) {
hasWitnesses = true;
+ } else if (
+ marker === Transaction.ADVANCED_TRANSACTION_MARKER &&
+ flag === Transaction.MWEB_PEGOUT_FLAG
+ ) {
+ hasMweb = true;
} else {
bufferReader.offset -= 2;
}
@@ -105,7 +116,7 @@ export class Transaction {
const voutLen = bufferReader.readVarInt();
for (let i = 0; i < voutLen; ++i) {
tx.outs.push({
- value: bufferReader.readUInt64(),
+ value: bufferReader.readInt64(),
script: bufferReader.readVarSlice(),
});
}
@@ -120,6 +131,11 @@ export class Transaction {
throw new Error('Transaction has superfluous witness data');
}
+ // MWEB peg-out transactions have an extra 0x00 byte before locktime
+ if (hasMweb) {
+ bufferReader.readUInt8();
+ }
+
tx.locktime = bufferReader.readUInt32();
if (_NO_STRICT) return tx;
@@ -130,11 +146,11 @@ export class Transaction {
}
static fromHex(hex: string): Transaction {
- return Transaction.fromBuffer(Buffer.from(hex, 'hex'), false);
+ return Transaction.fromBuffer(tools.fromHex(hex), false);
}
- static isCoinbaseHash(buffer: Buffer): boolean {
- typeforce(types.Hash256bit, buffer);
+ static isCoinbaseHash(buffer: Uint8Array): boolean {
+ v.parse(types.Hash256bitSchema, buffer);
for (let i = 0; i < 32; ++i) {
if (buffer[i] !== 0) return false;
}
@@ -153,22 +169,22 @@ export class Transaction {
}
addInput(
- hash: Buffer,
+ hash: Uint8Array,
index: number,
sequence?: number,
- scriptSig?: Buffer,
+ scriptSig?: Uint8Array,
): number {
- typeforce(
- types.tuple(
- types.Hash256bit,
- types.UInt32,
- types.maybe(types.UInt32),
- types.maybe(types.Buffer),
- ),
- arguments,
+ v.parse(
+ v.tuple([
+ types.Hash256bitSchema,
+ types.UInt32Schema,
+ v.nullable(v.optional(types.UInt32Schema)),
+ v.nullable(v.optional(types.BufferSchema)),
+ ]),
+ [hash, index, sequence, scriptSig],
);
- if (types.Null(sequence)) {
+ if (sequence === undefined || sequence === null) {
sequence = Transaction.DEFAULT_SEQUENCE;
}
@@ -184,8 +200,11 @@ export class Transaction {
);
}
- addOutput(scriptPubKey: Buffer, value: number): number {
- typeforce(types.tuple(types.Buffer, types.Satoshi), arguments);
+ addOutput(scriptPubKey: Uint8Array, value: bigint): number {
+ v.parse(v.tuple([types.BufferSchema, types.SatoshiSchema]), [
+ scriptPubKey,
+ value,
+ ]);
// Add the output and return the output's index
return (
@@ -202,9 +221,32 @@ export class Transaction {
});
}
+ /**
+ * Check if this is a Litecoin MWEB peg-out transaction.
+ * MWEB peg-out transactions have:
+ * - At least one output starting with OP_8 (witness version 8)
+ * - At least one input with empty script (anyone-can-spend from HogEx)
+ */
+ isMwebPegOutTx(): boolean {
+ return (
+ this.outs.some(output => {
+ // Check if output script starts with OP_8 (0x58)
+ return output.script.length > 0 && output.script[0] === opcodes.OP_8;
+ }) &&
+ this.ins.some(input => input.script.length === 0)
+ );
+ }
+
+ stripWitnesses(): void {
+ this.ins.forEach(input => {
+ input.witness = EMPTY_WITNESS; // Set witness data to an empty array
+ });
+ }
+
weight(): number {
- const base = this.byteLength(false);
- const total = this.byteLength(true);
+ // Weight calculation excludes MWEB-specific bytes
+ const base = this.byteLength(false, false);
+ const total = this.byteLength(true, false);
return base * 3 + total;
}
@@ -212,8 +254,9 @@ export class Transaction {
return Math.ceil(this.weight() / 4);
}
- byteLength(_ALLOW_WITNESS: boolean = true): number {
+ byteLength(_ALLOW_WITNESS: boolean = true, _ALLOW_MWEB: boolean = true): number {
const hasWitnesses = _ALLOW_WITNESS && this.hasWitnesses();
+ const hasMweb = _ALLOW_MWEB && !hasWitnesses && this.isMwebPegOutTx();
return (
(hasWitnesses ? 10 : 8) +
@@ -229,7 +272,8 @@ export class Transaction {
? this.ins.reduce((sum, input) => {
return sum + vectorSize(input.witness);
}, 0)
- : 0)
+ : 0) +
+ (hasMweb ? 3 : 0) // marker (1) + flag (1) + extra byte before locktime (1)
);
}
@@ -268,13 +312,14 @@ export class Transaction {
*/
hashForSignature(
inIndex: number,
- prevOutScript: Buffer,
+ prevOutScript: Uint8Array,
hashType: number,
- ): Buffer {
- typeforce(
- types.tuple(types.UInt32, types.Buffer, /* types.UInt8 */ types.Number),
- arguments,
- );
+ ): Uint8Array {
+ v.parse(v.tuple([types.UInt32Schema, types.BufferSchema, v.number()]), [
+ inIndex,
+ prevOutScript,
+ hashType,
+ ]);
// https://github.com/bitcoin/bitcoin/blob/master/src/test/sighash_tests.cpp#L29
if (inIndex >= this.ins.length) return ONE;
@@ -335,8 +380,8 @@ export class Transaction {
}
// serialize and hash
- const buffer: Buffer = Buffer.allocUnsafe(txTmp.byteLength(false) + 4);
- buffer.writeInt32LE(hashType, buffer.length - 4);
+ const buffer = new Uint8Array(txTmp.byteLength(false) + 4);
+ tools.writeInt32(buffer, buffer.length - 4, hashType, 'LE');
txTmp.__toBuffer(buffer, 0, false);
return bcrypto.hash256(buffer);
@@ -344,21 +389,21 @@ export class Transaction {
hashForWitnessV1(
inIndex: number,
- prevOutScripts: Buffer[],
- values: number[],
+ prevOutScripts: Uint8Array[],
+ values: bigint[],
hashType: number,
- leafHash?: Buffer,
- annex?: Buffer,
- ): Buffer {
+ leafHash?: Uint8Array,
+ annex?: Uint8Array,
+ ): Uint8Array {
// https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#common-signature-message
- typeforce(
- types.tuple(
- types.UInt32,
- typeforce.arrayOf(types.Buffer),
- typeforce.arrayOf(types.Satoshi),
- types.UInt32,
- ),
- arguments,
+ v.parse(
+ v.tuple([
+ types.UInt32Schema,
+ v.array(types.BufferSchema),
+ v.array(types.SatoshiSchema),
+ types.UInt32Schema,
+ ]),
+ [inIndex, prevOutScripts, values, hashType],
);
if (
@@ -391,11 +436,11 @@ export class Transaction {
bufferWriter.writeSlice(txIn.hash);
bufferWriter.writeUInt32(txIn.index);
});
- hashPrevouts = bcrypto.sha256(bufferWriter.end());
+ hashPrevouts = sha256(bufferWriter.end());
bufferWriter = BufferWriter.withCapacity(8 * this.ins.length);
- values.forEach(value => bufferWriter.writeUInt64(value));
- hashAmounts = bcrypto.sha256(bufferWriter.end());
+ values.forEach(value => bufferWriter.writeInt64(value));
+ hashAmounts = sha256(bufferWriter.end());
bufferWriter = BufferWriter.withCapacity(
prevOutScripts.map(varSliceSize).reduce((a, b) => a + b),
@@ -403,34 +448,36 @@ export class Transaction {
prevOutScripts.forEach(prevOutScript =>
bufferWriter.writeVarSlice(prevOutScript),
);
- hashScriptPubKeys = bcrypto.sha256(bufferWriter.end());
+ hashScriptPubKeys = sha256(bufferWriter.end());
bufferWriter = BufferWriter.withCapacity(4 * this.ins.length);
this.ins.forEach(txIn => bufferWriter.writeUInt32(txIn.sequence));
- hashSequences = bcrypto.sha256(bufferWriter.end());
+ hashSequences = sha256(bufferWriter.end());
}
if (!(isNone || isSingle)) {
+ if (!this.outs.length)
+ throw new Error('Add outputs to the transaction before signing.');
const txOutsSize = this.outs
.map(output => 8 + varSliceSize(output.script))
.reduce((a, b) => a + b);
const bufferWriter = BufferWriter.withCapacity(txOutsSize);
this.outs.forEach(out => {
- bufferWriter.writeUInt64(out.value);
+ bufferWriter.writeInt64(out.value);
bufferWriter.writeVarSlice(out.script);
});
- hashOutputs = bcrypto.sha256(bufferWriter.end());
+ hashOutputs = sha256(bufferWriter.end());
} else if (isSingle && inIndex < this.outs.length) {
const output = this.outs[inIndex];
const bufferWriter = BufferWriter.withCapacity(
8 + varSliceSize(output.script),
);
- bufferWriter.writeUInt64(output.value);
+ bufferWriter.writeInt64(output.value);
bufferWriter.writeVarSlice(output.script);
- hashOutputs = bcrypto.sha256(bufferWriter.end());
+ hashOutputs = sha256(bufferWriter.end());
}
const spendType = (leafHash ? 2 : 0) + (annex ? 1 : 0);
@@ -449,7 +496,7 @@ export class Transaction {
sigMsgWriter.writeUInt8(hashType);
// Transaction
- sigMsgWriter.writeInt32(this.version);
+ sigMsgWriter.writeUInt32(this.version);
sigMsgWriter.writeUInt32(this.locktime);
sigMsgWriter.writeSlice(hashPrevouts);
sigMsgWriter.writeSlice(hashAmounts);
@@ -464,7 +511,7 @@ export class Transaction {
const input = this.ins[inIndex];
sigMsgWriter.writeSlice(input.hash);
sigMsgWriter.writeUInt32(input.index);
- sigMsgWriter.writeUInt64(values[inIndex]);
+ sigMsgWriter.writeInt64(values[inIndex]);
sigMsgWriter.writeVarSlice(prevOutScripts[inIndex]);
sigMsgWriter.writeUInt32(input.sequence);
} else {
@@ -473,7 +520,7 @@ export class Transaction {
if (annex) {
const bufferWriter = BufferWriter.withCapacity(varSliceSize(annex));
bufferWriter.writeVarSlice(annex);
- sigMsgWriter.writeSlice(bcrypto.sha256(bufferWriter.end()));
+ sigMsgWriter.writeSlice(sha256(bufferWriter.end()));
}
// Output
if (isSingle) {
@@ -490,22 +537,27 @@ export class Transaction {
// https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#cite_note-19
return bcrypto.taggedHash(
'TapSighash',
- Buffer.concat([Buffer.of(0x00), sigMsgWriter.end()]),
+ tools.concat([Uint8Array.from([0x00]), sigMsgWriter.end()]),
);
}
hashForWitnessV0(
inIndex: number,
- prevOutScript: Buffer,
- value: number,
+ prevOutScript: Uint8Array,
+ value: bigint,
hashType: number,
- ): Buffer {
- typeforce(
- types.tuple(types.UInt32, types.Buffer, types.Satoshi, types.UInt32),
- arguments,
+ ): Uint8Array {
+ v.parse(
+ v.tuple([
+ types.UInt32Schema,
+ types.BufferSchema,
+ types.SatoshiSchema,
+ types.UInt32Schema,
+ ]),
+ [inIndex, prevOutScript, value, hashType],
);
- let tbuffer: Buffer = Buffer.from([]);
+ let tbuffer: Uint8Array = Uint8Array.from([]);
let bufferWriter: BufferWriter;
let hashOutputs = ZERO;
@@ -513,7 +565,7 @@ export class Transaction {
let hashSequence = ZERO;
if (!(hashType & Transaction.SIGHASH_ANYONECANPAY)) {
- tbuffer = Buffer.allocUnsafe(36 * this.ins.length);
+ tbuffer = new Uint8Array(36 * this.ins.length);
bufferWriter = new BufferWriter(tbuffer, 0);
this.ins.forEach(txIn => {
@@ -529,7 +581,7 @@ export class Transaction {
(hashType & 0x1f) !== Transaction.SIGHASH_SINGLE &&
(hashType & 0x1f) !== Transaction.SIGHASH_NONE
) {
- tbuffer = Buffer.allocUnsafe(4 * this.ins.length);
+ tbuffer = new Uint8Array(4 * this.ins.length);
bufferWriter = new BufferWriter(tbuffer, 0);
this.ins.forEach(txIn => {
@@ -547,11 +599,11 @@ export class Transaction {
return sum + 8 + varSliceSize(output.script);
}, 0);
- tbuffer = Buffer.allocUnsafe(txOutsSize);
+ tbuffer = new Uint8Array(txOutsSize);
bufferWriter = new BufferWriter(tbuffer, 0);
this.outs.forEach(out => {
- bufferWriter.writeUInt64(out.value);
+ bufferWriter.writeInt64(out.value);
bufferWriter.writeVarSlice(out.script);
});
@@ -562,25 +614,25 @@ export class Transaction {
) {
const output = this.outs[inIndex];
- tbuffer = Buffer.allocUnsafe(8 + varSliceSize(output.script));
+ tbuffer = new Uint8Array(8 + varSliceSize(output.script));
bufferWriter = new BufferWriter(tbuffer, 0);
- bufferWriter.writeUInt64(output.value);
+ bufferWriter.writeInt64(output.value);
bufferWriter.writeVarSlice(output.script);
hashOutputs = bcrypto.hash256(tbuffer);
}
- tbuffer = Buffer.allocUnsafe(156 + varSliceSize(prevOutScript));
+ tbuffer = new Uint8Array(156 + varSliceSize(prevOutScript));
bufferWriter = new BufferWriter(tbuffer, 0);
const input = this.ins[inIndex];
- bufferWriter.writeInt32(this.version);
+ bufferWriter.writeUInt32(this.version);
bufferWriter.writeSlice(hashPrevouts);
bufferWriter.writeSlice(hashSequence);
bufferWriter.writeSlice(input.hash);
bufferWriter.writeUInt32(input.index);
bufferWriter.writeVarSlice(prevOutScript);
- bufferWriter.writeUInt64(value);
+ bufferWriter.writeInt64(value);
bufferWriter.writeUInt32(input.sequence);
bufferWriter.writeSlice(hashOutputs);
bufferWriter.writeUInt32(this.locktime);
@@ -588,54 +640,66 @@ export class Transaction {
return bcrypto.hash256(tbuffer);
}
- getHash(forWitness?: boolean): Buffer {
+ getHash(forWitness?: boolean, forMweb?: boolean): Uint8Array {
// wtxid for coinbase is always 32 bytes of 0x00
- if (forWitness && this.isCoinbase()) return Buffer.alloc(32, 0);
- return bcrypto.hash256(this.__toBuffer(undefined, undefined, forWitness));
+ if (forWitness && this.isCoinbase()) return new Uint8Array(32);
+ // For MWEB transactions, txid is calculated WITHOUT the MWEB marker/flag/extra byte
+ // (similar to how SegWit txid excludes witness data)
+ const allowMweb = forMweb ?? false;
+ return bcrypto.hash256(this.__toBuffer(undefined, undefined, forWitness, allowMweb));
}
getId(): string {
// transaction hash's are displayed in reverse order
- return reverseBuffer(this.getHash(false)).toString('hex');
+ // txid calculation excludes both witness and MWEB-specific data
+ return tools.toHex(reverseBuffer(this.getHash(false, false)));
}
- toBuffer(buffer?: Buffer, initialOffset?: number): Buffer {
+ toBuffer(buffer?: Uint8Array, initialOffset?: number): Uint8Array {
return this.__toBuffer(buffer, initialOffset, true);
}
toHex(): string {
- return this.toBuffer(undefined, undefined).toString('hex');
+ return tools.toHex(this.toBuffer(undefined, undefined));
}
- setInputScript(index: number, scriptSig: Buffer): void {
- typeforce(types.tuple(types.Number, types.Buffer), arguments);
+ setInputScript(index: number, scriptSig: Uint8Array): void {
+ v.parse(v.tuple([v.number(), types.BufferSchema]), [index, scriptSig]);
this.ins[index].script = scriptSig;
}
- setWitness(index: number, witness: Buffer[]): void {
- typeforce(types.tuple(types.Number, [types.Buffer]), arguments);
+ setWitness(index: number, witness: Uint8Array[]): void {
+ v.parse(v.tuple([v.number(), v.array(types.BufferSchema)]), [
+ index,
+ witness,
+ ]);
this.ins[index].witness = witness;
}
private __toBuffer(
- buffer?: Buffer,
+ buffer?: Uint8Array,
initialOffset?: number,
_ALLOW_WITNESS: boolean = false,
- ): Buffer {
+ _ALLOW_MWEB: boolean = true,
+ ): Uint8Array {
if (!buffer)
- buffer = Buffer.allocUnsafe(this.byteLength(_ALLOW_WITNESS)) as Buffer;
+ buffer = new Uint8Array(this.byteLength(_ALLOW_WITNESS, _ALLOW_MWEB)) as Uint8Array;
const bufferWriter = new BufferWriter(buffer, initialOffset || 0);
- bufferWriter.writeInt32(this.version);
+ bufferWriter.writeUInt32(this.version);
const hasWitnesses = _ALLOW_WITNESS && this.hasWitnesses();
+ const hasMweb = _ALLOW_MWEB && !hasWitnesses && this.isMwebPegOutTx();
if (hasWitnesses) {
bufferWriter.writeUInt8(Transaction.ADVANCED_TRANSACTION_MARKER);
bufferWriter.writeUInt8(Transaction.ADVANCED_TRANSACTION_FLAG);
+ } else if (hasMweb) {
+ bufferWriter.writeUInt8(Transaction.ADVANCED_TRANSACTION_MARKER);
+ bufferWriter.writeUInt8(Transaction.MWEB_PEGOUT_FLAG);
}
bufferWriter.writeVarInt(this.ins.length);
@@ -650,7 +714,7 @@ export class Transaction {
bufferWriter.writeVarInt(this.outs.length);
this.outs.forEach(txOut => {
if (isOutput(txOut)) {
- bufferWriter.writeUInt64(txOut.value);
+ bufferWriter.writeInt64(txOut.value);
} else {
bufferWriter.writeSlice((txOut as any).valueBuffer);
}
@@ -664,6 +728,11 @@ export class Transaction {
});
}
+ // MWEB peg-out transactions have an extra 0x00 byte before locktime
+ if (hasMweb) {
+ bufferWriter.writeUInt8(0);
+ }
+
bufferWriter.writeUInt32(this.locktime);
// avoid slicing unless necessary
diff --git a/ts_src/types.ts b/ts_src/types.ts
index c035b4008..975f408c8 100644
--- a/ts_src/types.ts
+++ b/ts_src/types.ts
@@ -1,84 +1,119 @@
-import { Buffer as NBuffer } from 'buffer';
-export const typeforce = require('typeforce');
+import * as tools from 'uint8array-tools';
+import * as v from 'valibot';
-const ZERO32 = NBuffer.alloc(32, 0);
-const EC_P = NBuffer.from(
+const ZERO32 = new Uint8Array(32);
+const EC_P = tools.fromHex(
'fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f',
- 'hex',
);
-export function isPoint(p: Buffer | number | undefined | null): boolean {
- if (!NBuffer.isBuffer(p)) return false;
+
+export const NBufferSchemaFactory = (size: number) =>
+ v.pipe(v.instance(Uint8Array), v.length(size));
+
+/**
+ * Checks if two arrays of Buffers are equal.
+ * @param a - The first array of Buffers.
+ * @param b - The second array of Buffers.
+ * @returns True if the arrays are equal, false otherwise.
+ */
+export function stacksEqual(a: Uint8Array[], b: Uint8Array[]): boolean {
+ if (a.length !== b.length) return false;
+
+ return a.every((x, i) => {
+ return tools.compare(x, b[i]) === 0;
+ });
+}
+
+/**
+ * Checks if the given value is a valid elliptic curve point.
+ * @param p - The value to check.
+ * @returns True if the value is a valid elliptic curve point, false otherwise.
+ */
+export function isPoint(p: Uint8Array | number | undefined | null): boolean {
+ if (!(p instanceof Uint8Array)) return false;
if (p.length < 33) return false;
const t = p[0];
const x = p.slice(1, 33);
- if (x.compare(ZERO32) === 0) return false;
- if (x.compare(EC_P) >= 0) return false;
+ if (tools.compare(ZERO32, x) === 0) return false;
+ if (tools.compare(x, EC_P) >= 0) return false;
if ((t === 0x02 || t === 0x03) && p.length === 33) {
return true;
}
const y = p.slice(33);
- if (y.compare(ZERO32) === 0) return false;
- if (y.compare(EC_P) >= 0) return false;
+ if (tools.compare(ZERO32, y) === 0) return false;
+ if (tools.compare(y, EC_P) >= 0) return false;
if (t === 0x04 && p.length === 65) return true;
return false;
}
-const UINT31_MAX: number = Math.pow(2, 31) - 1;
-export function UInt31(value: number): boolean {
- return typeforce.UInt32(value) && value <= UINT31_MAX;
+export interface XOnlyPointAddTweakResult {
+ parity: 1 | 0;
+ xOnlyPubkey: Uint8Array;
}
-export function BIP32Path(value: string): boolean {
- return typeforce.String(value) && !!value.match(/^(m\/)?(\d+'?\/)*\d+'?$/);
+export interface Tapleaf {
+ output: Uint8Array;
+ version?: number;
}
-BIP32Path.toJSON = (): string => {
- return 'BIP32 derivation path';
-};
-export function Signer(obj: any): boolean {
- return (
- (typeforce.Buffer(obj.publicKey) ||
- typeof obj.getPublicKey === 'function') &&
- typeof obj.sign === 'function'
- );
+export const TAPLEAF_VERSION_MASK = 0xfe;
+export function isTapleaf(o: any): o is Tapleaf {
+ if (!o || !('output' in o)) return false;
+ if (!(o.output instanceof Uint8Array)) return false;
+ if (o.version !== undefined)
+ return (o.version & TAPLEAF_VERSION_MASK) === o.version;
+ return true;
}
-const SATOSHI_MAX: number = 21 * 1e14;
-export function Satoshi(value: number): boolean {
- return typeforce.UInt53(value) && value <= SATOSHI_MAX;
+/**
+ * Binary tree repsenting script path spends for a Taproot input.
+ * Each node is either a single Tapleaf, or a pair of Tapleaf | Taptree.
+ * The tree has no balancing requirements.
+ */
+export type Taptree = [Taptree | Tapleaf, Taptree | Tapleaf] | Tapleaf;
+
+export function isTaptree(scriptTree: any): scriptTree is Taptree {
+ if (!Array.isArray(scriptTree)) return isTapleaf(scriptTree);
+ if (scriptTree.length !== 2) return false;
+ return scriptTree.every((t: any) => isTaptree(t));
}
-// external dependent types
-export const ECPoint = typeforce.quacksLike('Point');
+export interface TinySecp256k1Interface {
+ isXOnlyPoint(p: Uint8Array): boolean;
+ xOnlyPointAddTweak(
+ p: Uint8Array,
+ tweak: Uint8Array,
+ ): XOnlyPointAddTweakResult | null;
+}
-// exposed, external API
-export const Network = typeforce.compile({
- messagePrefix: typeforce.oneOf(typeforce.Buffer, typeforce.String),
- bip32: {
- public: typeforce.UInt32,
- private: typeforce.UInt32,
- },
- pubKeyHash: typeforce.UInt8,
- scriptHash: typeforce.UInt8,
- wif: typeforce.UInt8,
-});
+export const Buffer256bitSchema = NBufferSchemaFactory(32);
+export const Hash160bitSchema = NBufferSchemaFactory(20);
+export const Hash256bitSchema = NBufferSchemaFactory(32);
+export const BufferSchema = v.instance(Uint8Array);
+export const HexSchema = v.pipe(v.string(), v.regex(/^([0-9a-f]{2})+$/i));
+export const UInt8Schema = v.pipe(
+ v.number(),
+ v.integer(),
+ v.minValue(0),
+ v.maxValue(0xff),
+);
+export const UInt32Schema = v.pipe(
+ v.number(),
+ v.integer(),
+ v.minValue(0),
+ v.maxValue(0xffffffff),
+);
+export const SatoshiSchema = v.pipe(
+ v.bigint(),
+ v.minValue(0n),
+ v.maxValue(0x7fff_ffff_ffff_ffffn),
+);
-export const Buffer256bit = typeforce.BufferN(32);
-export const Hash160bit = typeforce.BufferN(20);
-export const Hash256bit = typeforce.BufferN(32);
-export const Number = typeforce.Number; // tslint:disable-line variable-name
-export const Array = typeforce.Array;
-export const Boolean = typeforce.Boolean; // tslint:disable-line variable-name
-export const String = typeforce.String; // tslint:disable-line variable-name
-export const Buffer = typeforce.Buffer;
-export const Hex = typeforce.Hex;
-export const maybe = typeforce.maybe;
-export const tuple = typeforce.tuple;
-export const UInt8 = typeforce.UInt8;
-export const UInt32 = typeforce.UInt32;
-export const Function = typeforce.Function;
-export const BufferN = typeforce.BufferN;
-export const Null = typeforce.Null;
-export const oneOf = typeforce.oneOf;
+export const NullablePartial = (a: Record) =>
+ v.object(
+ Object.entries(a).reduce(
+ (acc, next) => ({ ...acc, [next[0]]: v.nullish(next[1]) }),
+ {},
+ ),
+ );
diff --git a/tsconfig.base.json b/tsconfig.base.json
new file mode 100644
index 000000000..61b3af5a0
--- /dev/null
+++ b/tsconfig.base.json
@@ -0,0 +1,24 @@
+{
+ "compilerOptions": {
+ "target": "ESNext",
+ "module": "commonjs",
+ "outDir": "./src",
+ "rootDir": "./ts_src",
+ "types": ["node"],
+ "allowJs": false,
+ "strict": true,
+ "noImplicitAny": true,
+ "strictNullChecks": true,
+ "strictFunctionTypes": true,
+ "strictBindCallApply": true,
+ "strictPropertyInitialization": true,
+ "noImplicitThis": true,
+ "alwaysStrict": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "moduleResolution": "Node"
+ },
+ "include": ["ts_src/**/*.ts"],
+ "exclude": ["node_modules/**/*"]
+ }
+
\ No newline at end of file
diff --git a/tsconfig.cjs.json b/tsconfig.cjs.json
new file mode 100644
index 000000000..e7c9acf6d
--- /dev/null
+++ b/tsconfig.cjs.json
@@ -0,0 +1,10 @@
+{
+ "extends": "./tsconfig.base.json",
+ "compilerOptions": {
+ "declaration": true,
+ "outDir": "src/cjs",
+ "module": "CommonJS",
+ "esModuleInterop": true,
+ }
+ }
+
\ No newline at end of file
diff --git a/tsconfig.json b/tsconfig.json
index 25f9d61c0..0d15b0248 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,31 +1,8 @@
{
+ "extends": "./tsconfig.base.json",
"compilerOptions": {
- "target": "ES2020",
- "module": "commonjs",
- "outDir": "./src",
- "declaration": true,
- "rootDir": "./ts_src",
- "types": [
- "node"
- ],
- "allowJs": false,
- "strict": true,
- "noImplicitAny": true,
- "strictNullChecks": true,
- "strictFunctionTypes": true,
- "strictBindCallApply": true,
- "strictPropertyInitialization": true,
- "noImplicitThis": true,
- "alwaysStrict": true,
- "esModuleInterop": false,
- "noUnusedLocals": true,
- "noUnusedParameters": true
+ "outDir": "src/esm",
+ "resolveJsonModule": true,
+ "module": "ESNext",
},
- "include": [
- "ts_src/**/*.ts"
- ],
- "exclude": [
- "**/*.spec.ts",
- "node_modules/**/*"
- ]
}
diff --git a/tslint.json b/tslint.json
deleted file mode 100644
index 90e513dfe..000000000
--- a/tslint.json
+++ /dev/null
@@ -1,40 +0,0 @@
-{
- "defaultSeverity": "error",
- "extends": ["tslint:recommended"],
- "rules": {
- "array-type": false,
- "arrow-parens": false,
- "curly": false,
- "indent": [
- true,
- "spaces",
- 2
- ],
- "interface-name": [false],
- "match-default-export-name": true,
- "max-classes-per-file": [false],
- "member-access": [true, "no-public"],
- "no-bitwise": false,
- "no-console": false,
- "no-empty": [true, "allow-empty-catch"],
- "no-implicit-dependencies": [true, "dev"],
- "no-return-await": true,
- "no-var-requires": false,
- "no-unused-expression": false,
- "object-literal-sort-keys": false,
- "quotemark": [true, "single", "avoid-escape"],
- "typedef": [
- true,
- "call-signature",
- "property-declaration"
- ],
- "variable-name": [
- true,
- "ban-keywords",
- "check-format",
- "allow-leading-underscore",
- "allow-pascal-case"
- ]
- },
- "rulesDirectory": []
-}
diff --git a/typedoc.json b/typedoc.json
new file mode 100644
index 000000000..041e85042
--- /dev/null
+++ b/typedoc.json
@@ -0,0 +1,17 @@
+{
+ "$schema": "https://typedoc.org/schema.json",
+ "name": "bitcoinjs-lib",
+ "entryPoints": ["./ts_src"],
+ "out": "docs",
+ "searchCategoryBoosts": {
+ "Component": 2,
+ "Model": 1.2
+ },
+ "searchGroupBoosts": {
+ "Classes": 1.5
+ },
+ "visibilityFilters": {},
+ "hideGenerator": true,
+ "excludePrivate": true,
+ "includeVersion": true
+}
\ No newline at end of file