├── .eslintrc
├── .github
└── workflows
│ └── main_ci.yml
├── .gitignore
├── .nsprc
├── .prettierignore
├── .prettierrc.json
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── package-lock.json
├── package.json
├── src
├── address.d.ts
├── address.js
├── bip66.d.ts
├── bip66.js
├── block.d.ts
├── block.js
├── bufferutils.d.ts
├── bufferutils.js
├── crypto.d.ts
├── crypto.js
├── ecc_lib.d.ts
├── ecc_lib.js
├── index.d.ts
├── index.js
├── merkle.d.ts
├── merkle.js
├── networks.d.ts
├── networks.js
├── ops.d.ts
├── ops.js
├── payments
│ ├── bip341.d.ts
│ ├── bip341.js
│ ├── embed.d.ts
│ ├── embed.js
│ ├── index.d.ts
│ ├── index.js
│ ├── lazy.d.ts
│ ├── lazy.js
│ ├── p2ms.d.ts
│ ├── p2ms.js
│ ├── p2pk.d.ts
│ ├── p2pk.js
│ ├── p2pkh.d.ts
│ ├── p2pkh.js
│ ├── p2sh.d.ts
│ ├── p2sh.js
│ ├── p2tr.d.ts
│ ├── p2tr.js
│ ├── p2wpkh.d.ts
│ ├── p2wpkh.js
│ ├── p2wsh.d.ts
│ └── p2wsh.js
├── psbt.d.ts
├── psbt.js
├── psbt
│ ├── bip371.d.ts
│ ├── bip371.js
│ ├── psbtutils.d.ts
│ └── psbtutils.js
├── push_data.d.ts
├── push_data.js
├── script.d.ts
├── script.js
├── script_number.d.ts
├── script_number.js
├── script_signature.d.ts
├── script_signature.js
├── transaction.d.ts
├── transaction.js
├── types.d.ts
└── types.js
├── test
├── address.spec.ts
├── bitcoin.core.spec.ts
├── block.spec.ts
├── bufferutils.spec.ts
├── crypto.spec.ts
├── fixtures
│ ├── address.json
│ ├── block.json
│ ├── bufferutils.json
│ ├── core
│ │ ├── README.md
│ │ ├── base58_encode_decode.json
│ │ ├── base58_keys_invalid.json
│ │ ├── base58_keys_valid.json
│ │ ├── blocks.json
│ │ ├── sig_canonical.json
│ │ ├── sig_noncanonical.json
│ │ ├── sighash.json
│ │ └── tx_valid.json
│ ├── crypto.json
│ ├── ecdsa.json
│ ├── ecpair.json
│ ├── embed.json
│ ├── p2ms.json
│ ├── p2pk.json
│ ├── p2pkh.json
│ ├── p2sh.json
│ ├── p2tr.json
│ ├── p2wpkh.json
│ ├── p2wsh.json
│ ├── psbt.json
│ ├── script.json
│ ├── script_number.json
│ ├── signature.json
│ └── transaction.json
├── integration
│ ├── _regtest.ts
│ ├── addresses.spec.ts
│ ├── bip32.spec.ts
│ ├── blocks.spec.ts
│ ├── cltv.spec.ts
│ ├── csv.spec.ts
│ ├── payments.spec.ts
│ ├── taproot.spec.ts
│ └── transactions.spec.ts
├── payments.spec.ts
├── payments.utils.ts
├── psbt.spec.ts
├── script.spec.ts
├── script_number.spec.ts
├── script_signature.spec.ts
├── transaction.spec.ts
├── ts-node-register.js
├── tsconfig.json
└── types.spec.ts
├── ts_src
├── address.ts
├── bip66.ts
├── block.ts
├── bufferutils.ts
├── crypto.ts
├── ecc_lib.ts
├── index.ts
├── merkle.ts
├── networks.ts
├── ops.ts
├── payments
│ ├── bip341.ts
│ ├── embed.ts
│ ├── index.ts
│ ├── lazy.ts
│ ├── p2ms.ts
│ ├── p2pk.ts
│ ├── p2pkh.ts
│ ├── p2sh.ts
│ ├── p2tr.ts
│ ├── p2wpkh.ts
│ └── p2wsh.ts
├── psbt.ts
├── psbt
│ ├── bip371.ts
│ └── psbtutils.ts
├── push_data.ts
├── script.ts
├── script_number.ts
├── script_signature.ts
├── transaction.ts
└── types.ts
└── tsconfig.json
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "root": true,
3 | "parser": "@typescript-eslint/parser",
4 | "plugins": [
5 | "@typescript-eslint"
6 | ],
7 | "extends": [
8 | "eslint:recommended",
9 | "prettier",
10 | "plugin:@typescript-eslint/recommended",
11 | "plugin:prettier/recommended"
12 | ],
13 | "rules": {
14 | "prettier/prettier": ["error", {
15 | "singleQuote": true,
16 | "trailingComma": "all",
17 |
18 | "endOfLine": "auto",
19 | "arrowParens": "avoid",
20 | "tabWidth": 2
21 | }],
22 |
23 | "arrow-body-style": "off",
24 | "prefer-arrow-callback": "off",
25 |
26 | "@typescript-eslint/array-type": 0,
27 | "@typescript-eslint/no-inferrable-types": "off",
28 | "@typescript-eslint/ban-types": "off",
29 | "@typescript-eslint/no-unused-vars": "off",
30 |
31 | "arrow-parens": "off",
32 | "curly": "off",
33 | "no-case-declarations": "off",
34 |
35 | "quotes": "off",
36 | "@typescript-eslint/quotes": ["error", "single", { "avoidEscape": true, "allowTemplateLiterals": true }],
37 | "prefer-rest-params": "off",
38 | "no-bitwise": "off",
39 | "no-console": "off",
40 | "no-empty": ["error", { "allowEmptyCatch": true }],
41 |
42 | "@typescript-eslint/no-var-requires": "off",
43 | "@typescript-eslint/no-non-null-assertion": "off",
44 | "@typescript-eslint/no-explicit-any": "off",
45 |
46 | "no-unused-expressions": "off",
47 | "@typescript-eslint/no-unused-expressions": "off",
48 |
49 | "space-before-function-paren": "off"
50 | },
51 | "env": {
52 | "browser": true,
53 | "amd": true,
54 | "node": true
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/.github/workflows/main_ci.yml:
--------------------------------------------------------------------------------
1 | name: Run Tests
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | pull_request:
8 |
9 | permissions:
10 | contents: read
11 |
12 | jobs:
13 | ##################
14 | # Jobs with matrix
15 | ##################
16 | unit:
17 | runs-on: ubuntu-latest
18 | strategy:
19 | fail-fast: false
20 | matrix:
21 | node-version: [14, 'lts/*']
22 | # See supported Node.js release schedule at https://nodejs.org/en/about/releases/
23 | steps:
24 | - uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 # v2
25 | - name: Use Node.js ${{ matrix.node-version }}
26 | uses: actions/setup-node@eeb10cff27034e7acf239c5d29f62154018672fd # v3
27 | with:
28 | node-version: ${{ matrix.node-version }}
29 | registry-url: https://registry.npmjs.org/
30 | cache: 'npm'
31 | - run: npm ci
32 | - run: npm run unit
33 | integration:
34 | runs-on: ubuntu-latest
35 | strategy:
36 | fail-fast: false
37 | matrix:
38 | node-version: [14, 'lts/*']
39 | # See supported Node.js release schedule at https://nodejs.org/en/about/releases/
40 | services:
41 | regtest:
42 | image: junderw/bitcoinjs-regtest-server@sha256:5b69cf95d9edf6d5b3a00504665d6b3c382a6aa3728fe8ce897974c519061463
43 | ports:
44 | - 8080:8080
45 | steps:
46 | - uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 # v2
47 | - name: Use Node.js ${{ matrix.node-version }}
48 | uses: actions/setup-node@eeb10cff27034e7acf239c5d29f62154018672fd # v3
49 | with:
50 | node-version: ${{ matrix.node-version }}
51 | registry-url: https://registry.npmjs.org/
52 | cache: 'npm'
53 | - run: npm ci
54 | - run: APIURL=http://127.0.0.1:8080/1 npm run integration
55 |
56 |
57 |
58 | #####################
59 | # Jobs without matrix
60 | #####################
61 | audit:
62 | runs-on: ubuntu-latest
63 | steps:
64 | - uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 # v2
65 | - uses: actions/setup-node@eeb10cff27034e7acf239c5d29f62154018672fd # v3
66 | with:
67 | node-version: 'lts/*'
68 | registry-url: https://registry.npmjs.org/
69 | cache: 'npm'
70 | - run: npm ci
71 | - run: npm run audit
72 | coverage:
73 | runs-on: ubuntu-latest
74 | steps:
75 | - uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 # v2
76 | - uses: actions/setup-node@eeb10cff27034e7acf239c5d29f62154018672fd # v3
77 | with:
78 | node-version: 'lts/*'
79 | registry-url: https://registry.npmjs.org/
80 | cache: 'npm'
81 | - run: npm ci
82 | - run: npm run coverage
83 | format:
84 | runs-on: ubuntu-latest
85 | steps:
86 | - uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 # v2
87 | - uses: actions/setup-node@eeb10cff27034e7acf239c5d29f62154018672fd # v3
88 | with:
89 | node-version: 'lts/*'
90 | registry-url: https://registry.npmjs.org/
91 | cache: 'npm'
92 | - run: npm ci
93 | - run: npm run format:ci
94 | gitdiff:
95 | runs-on: ubuntu-latest
96 | steps:
97 | - uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 # v2
98 | - uses: actions/setup-node@eeb10cff27034e7acf239c5d29f62154018672fd # v3
99 | with:
100 | node-version: 'lts/*'
101 | registry-url: https://registry.npmjs.org/
102 | cache: 'npm'
103 | - run: npm ci
104 | - run: npm run gitdiff:ci
105 | lint:
106 | runs-on: ubuntu-latest
107 | steps:
108 | - uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 # v2
109 | - uses: actions/setup-node@eeb10cff27034e7acf239c5d29f62154018672fd # v3
110 | with:
111 | node-version: 'lts/*'
112 | registry-url: https://registry.npmjs.org/
113 | cache: 'npm'
114 | - run: npm ci
115 | - run: npm run lint
116 | lint-tests:
117 | runs-on: ubuntu-latest
118 | steps:
119 | - uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 # v2
120 | - uses: actions/setup-node@eeb10cff27034e7acf239c5d29f62154018672fd # v3
121 | with:
122 | node-version: 'lts/*'
123 | registry-url: https://registry.npmjs.org/
124 | cache: 'npm'
125 | - run: npm ci
126 | - run: npm run lint:tests
127 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | coverage
2 | node_modules
3 | .nyc_output
4 | npm-debug.log
5 | test/*.js
6 | test/integration/*.js
7 | !test/ts-node-register.js
8 |
--------------------------------------------------------------------------------
/.nsprc:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | *.d.ts
--------------------------------------------------------------------------------
/.prettierrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true,
3 | "trailingComma": "all",
4 | "endOfLine": "auto",
5 | "arrowParens": "avoid",
6 | "tabWidth": 2
7 | }
8 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 |
2 | [//]: # (This is partially derived from https://github.com/bitcoin/bitcoin/blob/6579d80572d2d33aceabbd3db45a6a9f809aa5e3/CONTRIBUTING.md)
3 |
4 | # Contributing to bitcoinjs-lib
5 | Firstly in terms of structure, there is no particular concept of "bitcoinjs developers" in a sense of privileged people.
6 | Open source revolves around a meritocracy where contributors who help gain trust from the community.
7 |
8 | For practical purpose, there are repository "maintainers" who are responsible for merging pull requests.
9 |
10 | We are always accepting of pull requests, but we do adhere to specific standards in regards to coding style, test driven development and commit messages.
11 |
12 |
13 | ## Communication Channels
14 | GitHub is the preferred method of communication between members.
15 |
16 | Otherwise, in order of preference:
17 | * bitcoinjs.slack.com
18 | * #bitcoinjs-dev on Freenode IRC
19 |
20 |
21 | ## Workflow
22 | The codebase is maintained using the "contributor workflow" where everyone without exception contributes patch proposals using "pull requests".
23 | This facilitates social contribution, easy testing and peer review.
24 |
25 | To contribute a patch, the workflow is as follows:
26 |
27 | 1. Fork repository
28 | 1. Create topic branch
29 | 1. Commit patches
30 | 1. Push changes to your fork
31 | 1. Submit a pull request to https://github.com/bitcoinjs/bitcoinjs-lib
32 |
33 | [Commits should be atomic](https://en.wikipedia.org/wiki/Atomic_commit#Atomic_commit_convention) and diffs easy to read.
34 |
35 | If your pull request is accepted for merging, you may be asked by a maintainer to squash and or [rebase](https://git-scm.com/docs/git-rebase) your commits before it is merged.
36 |
37 | Please refrain from creating several pull requests for the same change.
38 |
39 | Patchsets should be focused:
40 |
41 | * Adding a feature, or
42 | * Fixing a bug, or
43 | * Refactoring code.
44 |
45 | If you combine these, the PR may be rejected or asked to be split up.
46 |
47 | The length of time required for peer review is unpredictable and will vary from pull request to pull request.
48 |
49 | Refer to the [Git manual](https://git-scm.com/doc) for any information about `git`.
50 |
51 |
52 | ## Regarding TypeScript
53 | 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.
54 |
55 | Some rules regarding TypeScript:
56 |
57 | * Modify the typescript source code in an IDE that will give you warnings for transpile/lint errors.
58 | * Once you are done with the modifications, run `npm run format` then `npm test`
59 | * Running the tests will transpile the ts files into js and d.ts files.
60 | * Use `git diff` or other tools to verify that the ts and js are changing the same parts.
61 | * Commit all changes to ts, js, and d.ts files.
62 | * Add tests where necessary.
63 | * Submit your pull request.
64 |
65 | Using TypeScript is for preventing bugs while writing code, as well as automatically generating type definitions. However, the JS file diffs must be verified, and any unverified JS will not be published to npm.
66 |
67 |
68 | ## We adhere to Bitcoin-Core policy
69 | Bitcoin script payment/script templates are based on community consensus, but typically adhere to bitcoin-core node policy by default.
70 |
71 | - `bitcoinjs.script.decompile` is consensus bound only, it does not reject based on policy.
72 | - `bitcoinjs.script.compile` will try to adhere to bitcoin-core `IsStandard` policies rules. (eg. minimalpush in https://github.com/bitcoinjs/bitcoinjs-lib/pull/638)
73 |
74 | Any elliptic curve `sign` operations should adhere to `IsStandard` policies, like `LOW_S`, but `verify` should not reject them [by default].
75 |
76 | If you need non-standard rejecting `decoding`, you should use an external module, not this library.
77 |
78 | #### TLDR
79 | Where "standards compliant" refers to the default policies of bitcoin-core, we adhere to the following:
80 | - Any "creation" event must create standards-compliant data (standards bound)
81 | - Any "validation" event must allow for non-standards compliant data (consensus bound)
82 |
83 | For stricter validation, use an external module which we [may have] provided.
84 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2011-2020 bitcoinjs-lib contributors
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "bitcoinjs-lib",
3 | "version": "6.1.3",
4 | "description": "Client-side Bitcoin JavaScript library",
5 | "main": "./src/index.js",
6 | "types": "./src/index.d.ts",
7 | "engines": {
8 | "node": ">=8.0.0"
9 | },
10 | "keywords": [
11 | "bitcoinjs",
12 | "bitcoin",
13 | "browserify",
14 | "javascript",
15 | "bitcoinjs"
16 | ],
17 | "scripts": {
18 | "audit": "better-npm-audit audit -l high",
19 | "build": "npm run clean && tsc -p ./tsconfig.json && npm run formatjs",
20 | "build:tests": "npm run clean:jstests && tsc -p ./test/tsconfig.json",
21 | "clean": "rimraf src",
22 | "clean:jstests": "rimraf 'test/**/!(ts-node-register)*.js'",
23 | "coverage-report": "npm run build && npm run nobuild:coverage-report",
24 | "coverage-html": "npm run build && npm run nobuild:coverage-html",
25 | "coverage": "npm run build && npm run nobuild:coverage",
26 | "format": "npm run prettier -- --write",
27 | "formatjs": "npm run prettierjs -- --write",
28 | "format:ci": "npm run prettier -- --check && npm run prettierjs -- --check",
29 | "gitdiff:ci": "npm run build && git diff --exit-code",
30 | "integration": "npm run build && npm run nobuild:integration",
31 | "lint": "eslint ts_src/** src/**/*.js",
32 | "lint:tests": "eslint test/**/*.spec.ts",
33 | "mocha:ts": "mocha --recursive --require test/ts-node-register",
34 | "nobuild:coverage-report": "nyc report --reporter=lcov",
35 | "nobuild:coverage-html": "nyc report --reporter=html",
36 | "nobuild:coverage": "npm run build:tests && nyc --check-coverage --branches 85 --functions 90 --lines 90 mocha && npm run clean:jstests",
37 | "nobuild:integration": "npm run mocha:ts -- --timeout 50000 'test/integration/*.ts'",
38 | "nobuild:unit": "npm run mocha:ts -- 'test/*.ts'",
39 | "prettier": "prettier \"ts_src/**/*.ts\" \"test/**/*.ts\" --ignore-path ./.prettierignore",
40 | "prettierjs": "prettier \"src/**/*.js\" --ignore-path ./.prettierignore",
41 | "test": "npm run build && npm run format:ci && npm run lint && npm run nobuild:coverage",
42 | "unit": "npm run build && npm run nobuild:unit"
43 | },
44 | "repository": {
45 | "type": "git",
46 | "url": "https://github.com/bitcoinjs/bitcoinjs-lib.git"
47 | },
48 | "files": [
49 | "src"
50 | ],
51 | "dependencies": {
52 | "@noble/hashes": "^1.2.0",
53 | "bech32": "^2.0.0",
54 | "bip174": "^2.1.0",
55 | "bs58check": "^3.0.1",
56 | "typeforce": "^1.11.3",
57 | "varuint-bitcoin": "^1.1.2"
58 | },
59 | "devDependencies": {
60 | "@types/bs58": "^4.0.0",
61 | "@types/bs58check": "^2.1.0",
62 | "@types/mocha": "^5.2.7",
63 | "@types/node": "^16.11.7",
64 | "@types/proxyquire": "^1.3.28",
65 | "@types/randombytes": "^2.0.0",
66 | "@typescript-eslint/eslint-plugin": "^5.45.0",
67 | "@typescript-eslint/parser": "^5.45.0",
68 | "better-npm-audit": "^3.7.3",
69 | "bip32": "^4.0.0",
70 | "bip39": "^3.1.0",
71 | "bip65": "^1.0.1",
72 | "bip68": "^1.0.3",
73 | "bs58": "^4.0.0",
74 | "dhttp": "^3.0.0",
75 | "ecpair": "^2.0.1",
76 | "eslint": "^8.29.0",
77 | "eslint-config-prettier": "^8.5.0",
78 | "eslint-plugin-prettier": "^4.2.1",
79 | "hoodwink": "^2.0.0",
80 | "minimaldata": "^1.0.2",
81 | "mocha": "^10.0.0",
82 | "nyc": "^15.1.0",
83 | "prettier": "^2.8.0",
84 | "proxyquire": "^2.0.1",
85 | "randombytes": "^2.1.0",
86 | "regtest-client": "0.2.0",
87 | "rimraf": "^2.6.3",
88 | "tiny-secp256k1": "^2.2.0",
89 | "ts-node": "^8.3.0",
90 | "typescript": "^4.4.4"
91 | },
92 | "license": "MIT"
93 | }
94 |
--------------------------------------------------------------------------------
/src/address.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | import { Network } from './networks';
3 | export interface Base58CheckResult {
4 | hash: Buffer;
5 | version: number;
6 | }
7 | export interface Bech32Result {
8 | version: number;
9 | prefix: string;
10 | data: Buffer;
11 | }
12 | export declare function fromBase58Check(address: string): Base58CheckResult;
13 | export declare function fromBech32(address: string): Bech32Result;
14 | export declare function toBase58Check(hash: Buffer, version: number): string;
15 | export declare function toBech32(data: Buffer, version: number, prefix: string): string;
16 | export declare function fromOutputScript(output: Buffer, network?: Network): string;
17 | export declare function toOutputScript(address: string, network?: Network): Buffer;
18 |
--------------------------------------------------------------------------------
/src/bip66.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | export declare function check(buffer: Buffer): boolean;
3 | export declare function decode(buffer: Buffer): {
4 | r: Buffer;
5 | s: Buffer;
6 | };
7 | export declare function encode(r: Buffer, s: Buffer): Buffer;
8 |
--------------------------------------------------------------------------------
/src/bip66.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | // Reference https://github.com/bitcoin/bips/blob/master/bip-0066.mediawiki
3 | // Format: 0x30 [total-length] 0x02 [R-length] [R] 0x02 [S-length] [S]
4 | // NOTE: SIGHASH byte ignored AND restricted, truncate before use
5 | Object.defineProperty(exports, '__esModule', { value: true });
6 | exports.encode = exports.decode = exports.check = void 0;
7 | function check(buffer) {
8 | if (buffer.length < 8) return false;
9 | if (buffer.length > 72) return false;
10 | if (buffer[0] !== 0x30) return false;
11 | if (buffer[1] !== buffer.length - 2) return false;
12 | if (buffer[2] !== 0x02) return false;
13 | const lenR = buffer[3];
14 | if (lenR === 0) return false;
15 | if (5 + lenR >= buffer.length) return false;
16 | if (buffer[4 + lenR] !== 0x02) return false;
17 | const lenS = buffer[5 + lenR];
18 | if (lenS === 0) return false;
19 | if (6 + lenR + lenS !== buffer.length) return false;
20 | if (buffer[4] & 0x80) return false;
21 | if (lenR > 1 && buffer[4] === 0x00 && !(buffer[5] & 0x80)) return false;
22 | if (buffer[lenR + 6] & 0x80) return false;
23 | if (lenS > 1 && buffer[lenR + 6] === 0x00 && !(buffer[lenR + 7] & 0x80))
24 | return false;
25 | return true;
26 | }
27 | exports.check = check;
28 | function decode(buffer) {
29 | if (buffer.length < 8) throw new Error('DER sequence length is too short');
30 | if (buffer.length > 72) throw new Error('DER sequence length is too long');
31 | if (buffer[0] !== 0x30) throw new Error('Expected DER sequence');
32 | if (buffer[1] !== buffer.length - 2)
33 | throw new Error('DER sequence length is invalid');
34 | if (buffer[2] !== 0x02) throw new Error('Expected DER integer');
35 | const lenR = buffer[3];
36 | if (lenR === 0) throw new Error('R length is zero');
37 | if (5 + lenR >= buffer.length) throw new Error('R length is too long');
38 | if (buffer[4 + lenR] !== 0x02) throw new Error('Expected DER integer (2)');
39 | const lenS = buffer[5 + lenR];
40 | if (lenS === 0) throw new Error('S length is zero');
41 | if (6 + lenR + lenS !== buffer.length) throw new Error('S length is invalid');
42 | if (buffer[4] & 0x80) throw new Error('R value is negative');
43 | if (lenR > 1 && buffer[4] === 0x00 && !(buffer[5] & 0x80))
44 | throw new Error('R value excessively padded');
45 | if (buffer[lenR + 6] & 0x80) throw new Error('S value is negative');
46 | if (lenS > 1 && buffer[lenR + 6] === 0x00 && !(buffer[lenR + 7] & 0x80))
47 | throw new Error('S value excessively padded');
48 | // non-BIP66 - extract R, S values
49 | return {
50 | r: buffer.slice(4, 4 + lenR),
51 | s: buffer.slice(6 + lenR),
52 | };
53 | }
54 | exports.decode = decode;
55 | /*
56 | * Expects r and s to be positive DER integers.
57 | *
58 | * The DER format uses the most significant bit as a sign bit (& 0x80).
59 | * If the significant bit is set AND the integer is positive, a 0x00 is prepended.
60 | *
61 | * Examples:
62 | *
63 | * 0 => 0x00
64 | * 1 => 0x01
65 | * -1 => 0xff
66 | * 127 => 0x7f
67 | * -127 => 0x81
68 | * 128 => 0x0080
69 | * -128 => 0x80
70 | * 255 => 0x00ff
71 | * -255 => 0xff01
72 | * 16300 => 0x3fac
73 | * -16300 => 0xc054
74 | * 62300 => 0x00f35c
75 | * -62300 => 0xff0ca4
76 | */
77 | function encode(r, s) {
78 | const lenR = r.length;
79 | const lenS = s.length;
80 | if (lenR === 0) throw new Error('R length is zero');
81 | if (lenS === 0) throw new Error('S length is zero');
82 | if (lenR > 33) throw new Error('R length is too long');
83 | if (lenS > 33) throw new Error('S length is too long');
84 | if (r[0] & 0x80) throw new Error('R value is negative');
85 | if (s[0] & 0x80) throw new Error('S value is negative');
86 | if (lenR > 1 && r[0] === 0x00 && !(r[1] & 0x80))
87 | throw new Error('R value excessively padded');
88 | if (lenS > 1 && s[0] === 0x00 && !(s[1] & 0x80))
89 | throw new Error('S value excessively padded');
90 | const signature = Buffer.allocUnsafe(6 + lenR + lenS);
91 | // 0x30 [total-length] 0x02 [R-length] [R] 0x02 [S-length] [S]
92 | signature[0] = 0x30;
93 | signature[1] = signature.length - 2;
94 | signature[2] = 0x02;
95 | signature[3] = r.length;
96 | r.copy(signature, 4);
97 | signature[4 + lenR] = 0x02;
98 | signature[5 + lenR] = s.length;
99 | s.copy(signature, 6 + lenR);
100 | return signature;
101 | }
102 | exports.encode = encode;
103 |
--------------------------------------------------------------------------------
/src/block.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | import { Transaction } from './transaction';
3 | export declare class Block {
4 | static fromBuffer(buffer: Buffer): Block;
5 | static fromHex(hex: string): Block;
6 | static calculateTarget(bits: number): Buffer;
7 | static calculateMerkleRoot(transactions: Transaction[], forWitness?: boolean): Buffer;
8 | version: number;
9 | prevHash?: Buffer;
10 | merkleRoot?: Buffer;
11 | timestamp: number;
12 | witnessCommit?: Buffer;
13 | bits: number;
14 | nonce: number;
15 | transactions?: Transaction[];
16 | getWitnessCommit(): Buffer | null;
17 | hasWitnessCommit(): boolean;
18 | hasWitness(): boolean;
19 | weight(): number;
20 | byteLength(headersOnly?: boolean, allowWitness?: boolean): number;
21 | getHash(): Buffer;
22 | getId(): string;
23 | getUTCDate(): Date;
24 | toBuffer(headersOnly?: boolean): Buffer;
25 | toHex(headersOnly?: boolean): string;
26 | checkTxRoots(): boolean;
27 | checkProofOfWork(): boolean;
28 | private __checkMerkleRoot;
29 | private __checkWitnessCommit;
30 | }
31 |
--------------------------------------------------------------------------------
/src/bufferutils.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | import * as varuint from 'varuint-bitcoin';
3 | export { varuint };
4 | export declare function readUInt64LE(buffer: Buffer, offset: number): number;
5 | export declare function writeUInt64LE(buffer: Buffer, value: number, offset: number): number;
6 | export declare function reverseBuffer(buffer: Buffer): Buffer;
7 | export declare function cloneBuffer(buffer: Buffer): Buffer;
8 | /**
9 | * Helper class for serialization of bitcoin data types into a pre-allocated buffer.
10 | */
11 | export declare class BufferWriter {
12 | buffer: Buffer;
13 | offset: number;
14 | static withCapacity(size: number): BufferWriter;
15 | constructor(buffer: Buffer, offset?: number);
16 | writeUInt8(i: number): void;
17 | writeInt32(i: number): void;
18 | writeUInt32(i: number): void;
19 | writeUInt64(i: number): void;
20 | writeVarInt(i: number): void;
21 | writeSlice(slice: Buffer): void;
22 | writeVarSlice(slice: Buffer): void;
23 | writeVector(vector: Buffer[]): void;
24 | end(): Buffer;
25 | }
26 | /**
27 | * Helper class for reading of bitcoin data types from a buffer.
28 | */
29 | export declare class BufferReader {
30 | buffer: Buffer;
31 | offset: number;
32 | constructor(buffer: Buffer, offset?: number);
33 | readUInt8(): number;
34 | readInt32(): number;
35 | readUInt32(): number;
36 | readUInt64(): number;
37 | readVarInt(): number;
38 | readSlice(n: number): Buffer;
39 | readVarSlice(): Buffer;
40 | readVector(): Buffer[];
41 | }
42 |
--------------------------------------------------------------------------------
/src/bufferutils.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | Object.defineProperty(exports, '__esModule', { value: true });
3 | exports.BufferReader =
4 | exports.BufferWriter =
5 | exports.cloneBuffer =
6 | exports.reverseBuffer =
7 | exports.writeUInt64LE =
8 | exports.readUInt64LE =
9 | exports.varuint =
10 | void 0;
11 | const types = require('./types');
12 | const { typeforce } = types;
13 | const varuint = require('varuint-bitcoin');
14 | exports.varuint = varuint;
15 | // https://github.com/feross/buffer/blob/master/index.js#L1127
16 | function verifuint(value, max) {
17 | if (typeof value !== 'number')
18 | throw new Error('cannot write a non-number as a number');
19 | if (value < 0)
20 | throw new Error('specified a negative value for writing an unsigned value');
21 | if (value > max) throw new Error('RangeError: value out of range');
22 | if (Math.floor(value) !== value)
23 | throw new Error('value has a fractional component');
24 | }
25 | function readUInt64LE(buffer, offset) {
26 | const a = buffer.readUInt32LE(offset);
27 | let b = buffer.readUInt32LE(offset + 4);
28 | b *= 0x100000000;
29 | verifuint(b + a, 0x001fffffffffffff);
30 | return b + a;
31 | }
32 | exports.readUInt64LE = readUInt64LE;
33 | function writeUInt64LE(buffer, value, offset) {
34 | verifuint(value, 0x001fffffffffffff);
35 | buffer.writeInt32LE(value & -1, offset);
36 | buffer.writeUInt32LE(Math.floor(value / 0x100000000), offset + 4);
37 | return offset + 8;
38 | }
39 | exports.writeUInt64LE = writeUInt64LE;
40 | function reverseBuffer(buffer) {
41 | if (buffer.length < 1) return buffer;
42 | let j = buffer.length - 1;
43 | let tmp = 0;
44 | for (let i = 0; i < buffer.length / 2; i++) {
45 | tmp = buffer[i];
46 | buffer[i] = buffer[j];
47 | buffer[j] = tmp;
48 | j--;
49 | }
50 | return buffer;
51 | }
52 | exports.reverseBuffer = reverseBuffer;
53 | function cloneBuffer(buffer) {
54 | const clone = Buffer.allocUnsafe(buffer.length);
55 | buffer.copy(clone);
56 | return clone;
57 | }
58 | exports.cloneBuffer = cloneBuffer;
59 | /**
60 | * Helper class for serialization of bitcoin data types into a pre-allocated buffer.
61 | */
62 | class BufferWriter {
63 | static withCapacity(size) {
64 | return new BufferWriter(Buffer.alloc(size));
65 | }
66 | constructor(buffer, offset = 0) {
67 | this.buffer = buffer;
68 | this.offset = offset;
69 | typeforce(types.tuple(types.Buffer, types.UInt32), [buffer, offset]);
70 | }
71 | writeUInt8(i) {
72 | this.offset = this.buffer.writeUInt8(i, this.offset);
73 | }
74 | writeInt32(i) {
75 | this.offset = this.buffer.writeInt32LE(i, this.offset);
76 | }
77 | writeUInt32(i) {
78 | this.offset = this.buffer.writeUInt32LE(i, this.offset);
79 | }
80 | writeUInt64(i) {
81 | this.offset = writeUInt64LE(this.buffer, i, this.offset);
82 | }
83 | writeVarInt(i) {
84 | varuint.encode(i, this.buffer, this.offset);
85 | this.offset += varuint.encode.bytes;
86 | }
87 | writeSlice(slice) {
88 | if (this.buffer.length < this.offset + slice.length) {
89 | throw new Error('Cannot write slice out of bounds');
90 | }
91 | this.offset += slice.copy(this.buffer, this.offset);
92 | }
93 | writeVarSlice(slice) {
94 | this.writeVarInt(slice.length);
95 | this.writeSlice(slice);
96 | }
97 | writeVector(vector) {
98 | this.writeVarInt(vector.length);
99 | vector.forEach(buf => this.writeVarSlice(buf));
100 | }
101 | end() {
102 | if (this.buffer.length === this.offset) {
103 | return this.buffer;
104 | }
105 | throw new Error(`buffer size ${this.buffer.length}, offset ${this.offset}`);
106 | }
107 | }
108 | exports.BufferWriter = BufferWriter;
109 | /**
110 | * Helper class for reading of bitcoin data types from a buffer.
111 | */
112 | class BufferReader {
113 | constructor(buffer, offset = 0) {
114 | this.buffer = buffer;
115 | this.offset = offset;
116 | typeforce(types.tuple(types.Buffer, types.UInt32), [buffer, offset]);
117 | }
118 | readUInt8() {
119 | const result = this.buffer.readUInt8(this.offset);
120 | this.offset++;
121 | return result;
122 | }
123 | readInt32() {
124 | const result = this.buffer.readInt32LE(this.offset);
125 | this.offset += 4;
126 | return result;
127 | }
128 | readUInt32() {
129 | const result = this.buffer.readUInt32LE(this.offset);
130 | this.offset += 4;
131 | return result;
132 | }
133 | readUInt64() {
134 | const result = readUInt64LE(this.buffer, this.offset);
135 | this.offset += 8;
136 | return result;
137 | }
138 | readVarInt() {
139 | const vi = varuint.decode(this.buffer, this.offset);
140 | this.offset += varuint.decode.bytes;
141 | return vi;
142 | }
143 | readSlice(n) {
144 | if (this.buffer.length < this.offset + n) {
145 | throw new Error('Cannot read slice out of bounds');
146 | }
147 | const result = this.buffer.slice(this.offset, this.offset + n);
148 | this.offset += n;
149 | return result;
150 | }
151 | readVarSlice() {
152 | return this.readSlice(this.readVarInt());
153 | }
154 | readVector() {
155 | const count = this.readVarInt();
156 | const vector = [];
157 | for (let i = 0; i < count; i++) vector.push(this.readVarSlice());
158 | return vector;
159 | }
160 | }
161 | exports.BufferReader = BufferReader;
162 |
--------------------------------------------------------------------------------
/src/crypto.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | export declare function ripemd160(buffer: Buffer): Buffer;
3 | export declare function sha1(buffer: Buffer): Buffer;
4 | export declare function sha256(buffer: Buffer): Buffer;
5 | export declare function hash160(buffer: Buffer): Buffer;
6 | export declare function hash256(buffer: Buffer): Buffer;
7 | export declare const TAGS: readonly ["BIP0340/challenge", "BIP0340/aux", "BIP0340/nonce", "TapLeaf", "TapBranch", "TapSighash", "TapTweak", "KeyAgg list", "KeyAgg coefficient"];
8 | export type TaggedHashPrefix = typeof TAGS[number];
9 | type TaggedHashPrefixes = {
10 | [key in TaggedHashPrefix]: Buffer;
11 | };
12 | /** An object mapping tags to their tagged hash prefix of [SHA256(tag) | SHA256(tag)] */
13 | export declare const TAGGED_HASH_PREFIXES: TaggedHashPrefixes;
14 | export declare function taggedHash(prefix: TaggedHashPrefix, data: Buffer): Buffer;
15 | export {};
16 |
--------------------------------------------------------------------------------
/src/crypto.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | Object.defineProperty(exports, '__esModule', { value: true });
3 | exports.taggedHash =
4 | exports.TAGGED_HASH_PREFIXES =
5 | exports.TAGS =
6 | exports.hash256 =
7 | exports.hash160 =
8 | exports.sha256 =
9 | exports.sha1 =
10 | exports.ripemd160 =
11 | void 0;
12 | const ripemd160_1 = require('@noble/hashes/ripemd160');
13 | const sha1_1 = require('@noble/hashes/sha1');
14 | const sha256_1 = require('@noble/hashes/sha256');
15 | function ripemd160(buffer) {
16 | return Buffer.from((0, ripemd160_1.ripemd160)(Uint8Array.from(buffer)));
17 | }
18 | exports.ripemd160 = ripemd160;
19 | function sha1(buffer) {
20 | return Buffer.from((0, sha1_1.sha1)(Uint8Array.from(buffer)));
21 | }
22 | exports.sha1 = sha1;
23 | function sha256(buffer) {
24 | return Buffer.from((0, sha256_1.sha256)(Uint8Array.from(buffer)));
25 | }
26 | exports.sha256 = sha256;
27 | function hash160(buffer) {
28 | return Buffer.from(
29 | (0, ripemd160_1.ripemd160)((0, sha256_1.sha256)(Uint8Array.from(buffer))),
30 | );
31 | }
32 | exports.hash160 = hash160;
33 | function hash256(buffer) {
34 | return Buffer.from(
35 | (0, sha256_1.sha256)((0, sha256_1.sha256)(Uint8Array.from(buffer))),
36 | );
37 | }
38 | exports.hash256 = hash256;
39 | exports.TAGS = [
40 | 'BIP0340/challenge',
41 | 'BIP0340/aux',
42 | 'BIP0340/nonce',
43 | 'TapLeaf',
44 | 'TapBranch',
45 | 'TapSighash',
46 | 'TapTweak',
47 | 'KeyAgg list',
48 | 'KeyAgg coefficient',
49 | ];
50 | /** An object mapping tags to their tagged hash prefix of [SHA256(tag) | SHA256(tag)] */
51 | exports.TAGGED_HASH_PREFIXES = {
52 | 'BIP0340/challenge': Buffer.from([
53 | 123, 181, 45, 122, 159, 239, 88, 50, 62, 177, 191, 122, 64, 125, 179, 130,
54 | 210, 243, 242, 216, 27, 177, 34, 79, 73, 254, 81, 143, 109, 72, 211, 124,
55 | 123, 181, 45, 122, 159, 239, 88, 50, 62, 177, 191, 122, 64, 125, 179, 130,
56 | 210, 243, 242, 216, 27, 177, 34, 79, 73, 254, 81, 143, 109, 72, 211, 124,
57 | ]),
58 | 'BIP0340/aux': Buffer.from([
59 | 241, 239, 78, 94, 192, 99, 202, 218, 109, 148, 202, 250, 157, 152, 126, 160,
60 | 105, 38, 88, 57, 236, 193, 31, 151, 45, 119, 165, 46, 216, 193, 204, 144,
61 | 241, 239, 78, 94, 192, 99, 202, 218, 109, 148, 202, 250, 157, 152, 126, 160,
62 | 105, 38, 88, 57, 236, 193, 31, 151, 45, 119, 165, 46, 216, 193, 204, 144,
63 | ]),
64 | 'BIP0340/nonce': Buffer.from([
65 | 7, 73, 119, 52, 167, 155, 203, 53, 91, 155, 140, 125, 3, 79, 18, 28, 244,
66 | 52, 215, 62, 247, 45, 218, 25, 135, 0, 97, 251, 82, 191, 235, 47, 7, 73,
67 | 119, 52, 167, 155, 203, 53, 91, 155, 140, 125, 3, 79, 18, 28, 244, 52, 215,
68 | 62, 247, 45, 218, 25, 135, 0, 97, 251, 82, 191, 235, 47,
69 | ]),
70 | TapLeaf: Buffer.from([
71 | 174, 234, 143, 220, 66, 8, 152, 49, 5, 115, 75, 88, 8, 29, 30, 38, 56, 211,
72 | 95, 28, 181, 64, 8, 212, 211, 87, 202, 3, 190, 120, 233, 238, 174, 234, 143,
73 | 220, 66, 8, 152, 49, 5, 115, 75, 88, 8, 29, 30, 38, 56, 211, 95, 28, 181,
74 | 64, 8, 212, 211, 87, 202, 3, 190, 120, 233, 238,
75 | ]),
76 | TapBranch: Buffer.from([
77 | 25, 65, 161, 242, 229, 110, 185, 95, 162, 169, 241, 148, 190, 92, 1, 247,
78 | 33, 111, 51, 237, 130, 176, 145, 70, 52, 144, 208, 91, 245, 22, 160, 21, 25,
79 | 65, 161, 242, 229, 110, 185, 95, 162, 169, 241, 148, 190, 92, 1, 247, 33,
80 | 111, 51, 237, 130, 176, 145, 70, 52, 144, 208, 91, 245, 22, 160, 21,
81 | ]),
82 | TapSighash: Buffer.from([
83 | 244, 10, 72, 223, 75, 42, 112, 200, 180, 146, 75, 242, 101, 70, 97, 237, 61,
84 | 149, 253, 102, 163, 19, 235, 135, 35, 117, 151, 198, 40, 228, 160, 49, 244,
85 | 10, 72, 223, 75, 42, 112, 200, 180, 146, 75, 242, 101, 70, 97, 237, 61, 149,
86 | 253, 102, 163, 19, 235, 135, 35, 117, 151, 198, 40, 228, 160, 49,
87 | ]),
88 | TapTweak: Buffer.from([
89 | 232, 15, 225, 99, 156, 156, 160, 80, 227, 175, 27, 57, 193, 67, 198, 62, 66,
90 | 156, 188, 235, 21, 217, 64, 251, 181, 197, 161, 244, 175, 87, 197, 233, 232,
91 | 15, 225, 99, 156, 156, 160, 80, 227, 175, 27, 57, 193, 67, 198, 62, 66, 156,
92 | 188, 235, 21, 217, 64, 251, 181, 197, 161, 244, 175, 87, 197, 233,
93 | ]),
94 | 'KeyAgg list': Buffer.from([
95 | 72, 28, 151, 28, 60, 11, 70, 215, 240, 178, 117, 174, 89, 141, 78, 44, 126,
96 | 215, 49, 156, 89, 74, 92, 110, 199, 158, 160, 212, 153, 2, 148, 240, 72, 28,
97 | 151, 28, 60, 11, 70, 215, 240, 178, 117, 174, 89, 141, 78, 44, 126, 215, 49,
98 | 156, 89, 74, 92, 110, 199, 158, 160, 212, 153, 2, 148, 240,
99 | ]),
100 | 'KeyAgg coefficient': Buffer.from([
101 | 191, 201, 4, 3, 77, 28, 136, 232, 200, 14, 34, 229, 61, 36, 86, 109, 100,
102 | 130, 78, 214, 66, 114, 129, 192, 145, 0, 249, 77, 205, 82, 201, 129, 191,
103 | 201, 4, 3, 77, 28, 136, 232, 200, 14, 34, 229, 61, 36, 86, 109, 100, 130,
104 | 78, 214, 66, 114, 129, 192, 145, 0, 249, 77, 205, 82, 201, 129,
105 | ]),
106 | };
107 | function taggedHash(prefix, data) {
108 | return sha256(Buffer.concat([exports.TAGGED_HASH_PREFIXES[prefix], data]));
109 | }
110 | exports.taggedHash = taggedHash;
111 |
--------------------------------------------------------------------------------
/src/ecc_lib.d.ts:
--------------------------------------------------------------------------------
1 | import { TinySecp256k1Interface } from './types';
2 | export declare function initEccLib(eccLib: TinySecp256k1Interface | undefined): void;
3 | export declare function getEccLib(): TinySecp256k1Interface;
4 |
--------------------------------------------------------------------------------
/src/ecc_lib.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | Object.defineProperty(exports, '__esModule', { value: true });
3 | exports.getEccLib = exports.initEccLib = void 0;
4 | const _ECCLIB_CACHE = {};
5 | function initEccLib(eccLib) {
6 | if (!eccLib) {
7 | // allow clearing the library
8 | _ECCLIB_CACHE.eccLib = eccLib;
9 | } else if (eccLib !== _ECCLIB_CACHE.eccLib) {
10 | // new instance, verify it
11 | verifyEcc(eccLib);
12 | _ECCLIB_CACHE.eccLib = eccLib;
13 | }
14 | }
15 | exports.initEccLib = initEccLib;
16 | function getEccLib() {
17 | if (!_ECCLIB_CACHE.eccLib)
18 | throw new Error(
19 | 'No ECC Library provided. You must call initEccLib() with a valid TinySecp256k1Interface instance',
20 | );
21 | return _ECCLIB_CACHE.eccLib;
22 | }
23 | exports.getEccLib = getEccLib;
24 | const h = hex => Buffer.from(hex, 'hex');
25 | function verifyEcc(ecc) {
26 | assert(typeof ecc.isXOnlyPoint === 'function');
27 | assert(
28 | ecc.isXOnlyPoint(
29 | h('79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798'),
30 | ),
31 | );
32 | assert(
33 | ecc.isXOnlyPoint(
34 | h('fffffffffffffffffffffffffffffffffffffffffffffffffffffffeeffffc2e'),
35 | ),
36 | );
37 | assert(
38 | ecc.isXOnlyPoint(
39 | h('f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9'),
40 | ),
41 | );
42 | assert(
43 | ecc.isXOnlyPoint(
44 | h('0000000000000000000000000000000000000000000000000000000000000001'),
45 | ),
46 | );
47 | assert(
48 | !ecc.isXOnlyPoint(
49 | h('0000000000000000000000000000000000000000000000000000000000000000'),
50 | ),
51 | );
52 | assert(
53 | !ecc.isXOnlyPoint(
54 | h('fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f'),
55 | ),
56 | );
57 | assert(typeof ecc.xOnlyPointAddTweak === 'function');
58 | tweakAddVectors.forEach(t => {
59 | const r = ecc.xOnlyPointAddTweak(h(t.pubkey), h(t.tweak));
60 | if (t.result === null) {
61 | assert(r === null);
62 | } else {
63 | assert(r !== null);
64 | assert(r.parity === t.parity);
65 | assert(Buffer.from(r.xOnlyPubkey).equals(h(t.result)));
66 | }
67 | });
68 | }
69 | function assert(bool) {
70 | if (!bool) throw new Error('ecc library invalid');
71 | }
72 | const tweakAddVectors = [
73 | {
74 | pubkey: '79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798',
75 | tweak: 'fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140',
76 | parity: -1,
77 | result: null,
78 | },
79 | {
80 | pubkey: '1617d38ed8d8657da4d4761e8057bc396ea9e4b9d29776d4be096016dbd2509b',
81 | tweak: 'a8397a935f0dfceba6ba9618f6451ef4d80637abf4e6af2669fbc9de6a8fd2ac',
82 | parity: 1,
83 | result: 'e478f99dab91052ab39a33ea35fd5e6e4933f4d28023cd597c9a1f6760346adf',
84 | },
85 | {
86 | pubkey: '2c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e668680991',
87 | tweak: '823c3cd2142744b075a87eade7e1b8678ba308d566226a0056ca2b7a76f86b47',
88 | parity: 0,
89 | result: '9534f8dc8c6deda2dc007655981c78b49c5d96c778fbf363462a11ec9dfd948c',
90 | },
91 | ];
92 |
--------------------------------------------------------------------------------
/src/index.d.ts:
--------------------------------------------------------------------------------
1 | import * as address from './address';
2 | import * as crypto from './crypto';
3 | import * as networks from './networks';
4 | import * as payments from './payments';
5 | import * as script from './script';
6 | export { address, crypto, networks, payments, script };
7 | export { Block } from './block';
8 | export { TaggedHashPrefix } from './crypto';
9 | export { Psbt, PsbtTxInput, PsbtTxOutput, Signer, SignerAsync, HDSigner, HDSignerAsync, } from './psbt';
10 | export { OPS as opcodes } from './ops';
11 | export { Transaction } from './transaction';
12 | export { Network } from './networks';
13 | export { Payment, PaymentCreator, PaymentOpts, Stack, StackElement, } from './payments';
14 | export { Input as TxInput, Output as TxOutput } from './transaction';
15 | export { initEccLib } from './ecc_lib';
16 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | Object.defineProperty(exports, '__esModule', { value: true });
3 | exports.initEccLib =
4 | exports.Transaction =
5 | exports.opcodes =
6 | exports.Psbt =
7 | exports.Block =
8 | exports.script =
9 | exports.payments =
10 | exports.networks =
11 | exports.crypto =
12 | exports.address =
13 | void 0;
14 | const address = require('./address');
15 | exports.address = address;
16 | const crypto = require('./crypto');
17 | exports.crypto = crypto;
18 | const networks = require('./networks');
19 | exports.networks = networks;
20 | const payments = require('./payments');
21 | exports.payments = payments;
22 | const script = require('./script');
23 | exports.script = script;
24 | var block_1 = require('./block');
25 | Object.defineProperty(exports, 'Block', {
26 | enumerable: true,
27 | get: function () {
28 | return block_1.Block;
29 | },
30 | });
31 | var psbt_1 = require('./psbt');
32 | Object.defineProperty(exports, 'Psbt', {
33 | enumerable: true,
34 | get: function () {
35 | return psbt_1.Psbt;
36 | },
37 | });
38 | var ops_1 = require('./ops');
39 | Object.defineProperty(exports, 'opcodes', {
40 | enumerable: true,
41 | get: function () {
42 | return ops_1.OPS;
43 | },
44 | });
45 | var transaction_1 = require('./transaction');
46 | Object.defineProperty(exports, 'Transaction', {
47 | enumerable: true,
48 | get: function () {
49 | return transaction_1.Transaction;
50 | },
51 | });
52 | var ecc_lib_1 = require('./ecc_lib');
53 | Object.defineProperty(exports, 'initEccLib', {
54 | enumerable: true,
55 | get: function () {
56 | return ecc_lib_1.initEccLib;
57 | },
58 | });
59 |
--------------------------------------------------------------------------------
/src/merkle.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | export declare function fastMerkleRoot(values: Buffer[], digestFn: (b: Buffer) => Buffer): Buffer;
3 |
--------------------------------------------------------------------------------
/src/merkle.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | Object.defineProperty(exports, '__esModule', { value: true });
3 | exports.fastMerkleRoot = void 0;
4 | function fastMerkleRoot(values, digestFn) {
5 | if (!Array.isArray(values)) throw TypeError('Expected values Array');
6 | if (typeof digestFn !== 'function')
7 | throw TypeError('Expected digest Function');
8 | let length = values.length;
9 | const results = values.concat();
10 | while (length > 1) {
11 | let j = 0;
12 | for (let i = 0; i < length; i += 2, ++j) {
13 | const left = results[i];
14 | const right = i + 1 === length ? left : results[i + 1];
15 | const data = Buffer.concat([left, right]);
16 | results[j] = digestFn(data);
17 | }
18 | length = j;
19 | }
20 | return results[0];
21 | }
22 | exports.fastMerkleRoot = fastMerkleRoot;
23 |
--------------------------------------------------------------------------------
/src/networks.d.ts:
--------------------------------------------------------------------------------
1 | export interface Network {
2 | messagePrefix: string;
3 | bech32: string;
4 | bip32: Bip32;
5 | pubKeyHash: number;
6 | scriptHash: number;
7 | wif: number;
8 | }
9 | interface Bip32 {
10 | public: number;
11 | private: number;
12 | }
13 | export declare const bitcoin: Network;
14 | export declare const regtest: Network;
15 | export declare const testnet: Network;
16 | export {};
17 |
--------------------------------------------------------------------------------
/src/networks.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | Object.defineProperty(exports, '__esModule', { value: true });
3 | exports.testnet = exports.regtest = exports.bitcoin = void 0;
4 | exports.bitcoin = {
5 | messagePrefix: '\x18Bitcoin Signed Message:\n',
6 | bech32: 'bc',
7 | bip32: {
8 | public: 0x0488b21e,
9 | private: 0x0488ade4,
10 | },
11 | pubKeyHash: 0x00,
12 | scriptHash: 0x05,
13 | wif: 0x80,
14 | };
15 | exports.regtest = {
16 | messagePrefix: '\x18Bitcoin Signed Message:\n',
17 | bech32: 'bcrt',
18 | bip32: {
19 | public: 0x043587cf,
20 | private: 0x04358394,
21 | },
22 | pubKeyHash: 0x6f,
23 | scriptHash: 0xc4,
24 | wif: 0xef,
25 | };
26 | exports.testnet = {
27 | messagePrefix: '\x18Bitcoin Signed Message:\n',
28 | bech32: 'tb',
29 | bip32: {
30 | public: 0x043587cf,
31 | private: 0x04358394,
32 | },
33 | pubKeyHash: 0x6f,
34 | scriptHash: 0xc4,
35 | wif: 0xef,
36 | };
37 |
--------------------------------------------------------------------------------
/src/ops.d.ts:
--------------------------------------------------------------------------------
1 | declare const OPS: {
2 | [key: string]: number;
3 | };
4 | declare const REVERSE_OPS: {
5 | [key: number]: string;
6 | };
7 | export { OPS, REVERSE_OPS };
8 |
--------------------------------------------------------------------------------
/src/ops.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | Object.defineProperty(exports, '__esModule', { value: true });
3 | exports.REVERSE_OPS = exports.OPS = void 0;
4 | const OPS = {
5 | OP_FALSE: 0,
6 | OP_0: 0,
7 | OP_PUSHDATA1: 76,
8 | OP_PUSHDATA2: 77,
9 | OP_PUSHDATA4: 78,
10 | OP_1NEGATE: 79,
11 | OP_RESERVED: 80,
12 | OP_TRUE: 81,
13 | OP_1: 81,
14 | OP_2: 82,
15 | OP_3: 83,
16 | OP_4: 84,
17 | OP_5: 85,
18 | OP_6: 86,
19 | OP_7: 87,
20 | OP_8: 88,
21 | OP_9: 89,
22 | OP_10: 90,
23 | OP_11: 91,
24 | OP_12: 92,
25 | OP_13: 93,
26 | OP_14: 94,
27 | OP_15: 95,
28 | OP_16: 96,
29 | OP_NOP: 97,
30 | OP_VER: 98,
31 | OP_IF: 99,
32 | OP_NOTIF: 100,
33 | OP_VERIF: 101,
34 | OP_VERNOTIF: 102,
35 | OP_ELSE: 103,
36 | OP_ENDIF: 104,
37 | OP_VERIFY: 105,
38 | OP_RETURN: 106,
39 | OP_TOALTSTACK: 107,
40 | OP_FROMALTSTACK: 108,
41 | OP_2DROP: 109,
42 | OP_2DUP: 110,
43 | OP_3DUP: 111,
44 | OP_2OVER: 112,
45 | OP_2ROT: 113,
46 | OP_2SWAP: 114,
47 | OP_IFDUP: 115,
48 | OP_DEPTH: 116,
49 | OP_DROP: 117,
50 | OP_DUP: 118,
51 | OP_NIP: 119,
52 | OP_OVER: 120,
53 | OP_PICK: 121,
54 | OP_ROLL: 122,
55 | OP_ROT: 123,
56 | OP_SWAP: 124,
57 | OP_TUCK: 125,
58 | OP_CAT: 126,
59 | OP_SUBSTR: 127,
60 | OP_LEFT: 128,
61 | OP_RIGHT: 129,
62 | OP_SIZE: 130,
63 | OP_INVERT: 131,
64 | OP_AND: 132,
65 | OP_OR: 133,
66 | OP_XOR: 134,
67 | OP_EQUAL: 135,
68 | OP_EQUALVERIFY: 136,
69 | OP_RESERVED1: 137,
70 | OP_RESERVED2: 138,
71 | OP_1ADD: 139,
72 | OP_1SUB: 140,
73 | OP_2MUL: 141,
74 | OP_2DIV: 142,
75 | OP_NEGATE: 143,
76 | OP_ABS: 144,
77 | OP_NOT: 145,
78 | OP_0NOTEQUAL: 146,
79 | OP_ADD: 147,
80 | OP_SUB: 148,
81 | OP_MUL: 149,
82 | OP_DIV: 150,
83 | OP_MOD: 151,
84 | OP_LSHIFT: 152,
85 | OP_RSHIFT: 153,
86 | OP_BOOLAND: 154,
87 | OP_BOOLOR: 155,
88 | OP_NUMEQUAL: 156,
89 | OP_NUMEQUALVERIFY: 157,
90 | OP_NUMNOTEQUAL: 158,
91 | OP_LESSTHAN: 159,
92 | OP_GREATERTHAN: 160,
93 | OP_LESSTHANOREQUAL: 161,
94 | OP_GREATERTHANOREQUAL: 162,
95 | OP_MIN: 163,
96 | OP_MAX: 164,
97 | OP_WITHIN: 165,
98 | OP_RIPEMD160: 166,
99 | OP_SHA1: 167,
100 | OP_SHA256: 168,
101 | OP_HASH160: 169,
102 | OP_HASH256: 170,
103 | OP_CODESEPARATOR: 171,
104 | OP_CHECKSIG: 172,
105 | OP_CHECKSIGVERIFY: 173,
106 | OP_CHECKMULTISIG: 174,
107 | OP_CHECKMULTISIGVERIFY: 175,
108 | OP_NOP1: 176,
109 | OP_NOP2: 177,
110 | OP_CHECKLOCKTIMEVERIFY: 177,
111 | OP_NOP3: 178,
112 | OP_CHECKSEQUENCEVERIFY: 178,
113 | OP_NOP4: 179,
114 | OP_NOP5: 180,
115 | OP_NOP6: 181,
116 | OP_NOP7: 182,
117 | OP_NOP8: 183,
118 | OP_NOP9: 184,
119 | OP_NOP10: 185,
120 | OP_CHECKSIGADD: 186,
121 | OP_PUBKEYHASH: 253,
122 | OP_PUBKEY: 254,
123 | OP_INVALIDOPCODE: 255,
124 | };
125 | exports.OPS = OPS;
126 | const REVERSE_OPS = {};
127 | exports.REVERSE_OPS = REVERSE_OPS;
128 | for (const op of Object.keys(OPS)) {
129 | const code = OPS[op];
130 | REVERSE_OPS[code] = op;
131 | }
132 |
--------------------------------------------------------------------------------
/src/payments/bip341.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | import { Tapleaf, Taptree } from '../types';
3 | export declare const LEAF_VERSION_TAPSCRIPT = 192;
4 | export declare const MAX_TAPTREE_DEPTH = 128;
5 | interface HashLeaf {
6 | hash: Buffer;
7 | }
8 | interface HashBranch {
9 | hash: Buffer;
10 | left: HashTree;
11 | right: HashTree;
12 | }
13 | interface TweakedPublicKey {
14 | parity: number;
15 | x: Buffer;
16 | }
17 | /**
18 | * Binary tree representing leaf, branch, and root node hashes of a Taptree.
19 | * Each node contains a hash, and potentially left and right branch hashes.
20 | * This tree is used for 2 purposes: Providing the root hash for tweaking,
21 | * and calculating merkle inclusion proofs when constructing a control block.
22 | */
23 | export type HashTree = HashLeaf | HashBranch;
24 | export declare function rootHashFromPath(controlBlock: Buffer, leafHash: Buffer): Buffer;
25 | /**
26 | * Build a hash tree of merkle nodes from the scripts binary tree.
27 | * @param scriptTree - the tree of scripts to pairwise hash.
28 | */
29 | export declare function toHashTree(scriptTree: Taptree): HashTree;
30 | /**
31 | * Given a HashTree, finds the path from a particular hash to the root.
32 | * @param node - the root of the tree
33 | * @param hash - the hash to search for
34 | * @returns - array of sibling hashes, from leaf (inclusive) to root
35 | * (exclusive) needed to prove inclusion of the specified hash. undefined if no
36 | * path is found
37 | */
38 | export declare function findScriptPath(node: HashTree, hash: Buffer): Buffer[] | undefined;
39 | export declare function tapleafHash(leaf: Tapleaf): Buffer;
40 | export declare function tapTweakHash(pubKey: Buffer, h: Buffer | undefined): Buffer;
41 | export declare function tweakKey(pubKey: Buffer, h: Buffer | undefined): TweakedPublicKey | null;
42 | export {};
43 |
--------------------------------------------------------------------------------
/src/payments/bip341.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | Object.defineProperty(exports, '__esModule', { value: true });
3 | exports.tweakKey =
4 | exports.tapTweakHash =
5 | exports.tapleafHash =
6 | exports.findScriptPath =
7 | exports.toHashTree =
8 | exports.rootHashFromPath =
9 | exports.MAX_TAPTREE_DEPTH =
10 | exports.LEAF_VERSION_TAPSCRIPT =
11 | void 0;
12 | const buffer_1 = require('buffer');
13 | const ecc_lib_1 = require('../ecc_lib');
14 | const bcrypto = require('../crypto');
15 | const bufferutils_1 = require('../bufferutils');
16 | const types_1 = require('../types');
17 | exports.LEAF_VERSION_TAPSCRIPT = 0xc0;
18 | exports.MAX_TAPTREE_DEPTH = 128;
19 | const isHashBranch = ht => 'left' in ht && 'right' in ht;
20 | function rootHashFromPath(controlBlock, leafHash) {
21 | if (controlBlock.length < 33)
22 | throw new TypeError(
23 | `The control-block length is too small. Got ${controlBlock.length}, expected min 33.`,
24 | );
25 | const m = (controlBlock.length - 33) / 32;
26 | let kj = leafHash;
27 | for (let j = 0; j < m; j++) {
28 | const ej = controlBlock.slice(33 + 32 * j, 65 + 32 * j);
29 | if (kj.compare(ej) < 0) {
30 | kj = tapBranchHash(kj, ej);
31 | } else {
32 | kj = tapBranchHash(ej, kj);
33 | }
34 | }
35 | return kj;
36 | }
37 | exports.rootHashFromPath = rootHashFromPath;
38 | /**
39 | * Build a hash tree of merkle nodes from the scripts binary tree.
40 | * @param scriptTree - the tree of scripts to pairwise hash.
41 | */
42 | function toHashTree(scriptTree) {
43 | if ((0, types_1.isTapleaf)(scriptTree))
44 | return { hash: tapleafHash(scriptTree) };
45 | const hashes = [toHashTree(scriptTree[0]), toHashTree(scriptTree[1])];
46 | hashes.sort((a, b) => a.hash.compare(b.hash));
47 | const [left, right] = hashes;
48 | return {
49 | hash: tapBranchHash(left.hash, right.hash),
50 | left,
51 | right,
52 | };
53 | }
54 | exports.toHashTree = toHashTree;
55 | /**
56 | * Given a HashTree, finds the path from a particular hash to the root.
57 | * @param node - the root of the tree
58 | * @param hash - the hash to search for
59 | * @returns - array of sibling hashes, from leaf (inclusive) to root
60 | * (exclusive) needed to prove inclusion of the specified hash. undefined if no
61 | * path is found
62 | */
63 | function findScriptPath(node, hash) {
64 | if (isHashBranch(node)) {
65 | const leftPath = findScriptPath(node.left, hash);
66 | if (leftPath !== undefined) return [...leftPath, node.right.hash];
67 | const rightPath = findScriptPath(node.right, hash);
68 | if (rightPath !== undefined) return [...rightPath, node.left.hash];
69 | } else if (node.hash.equals(hash)) {
70 | return [];
71 | }
72 | return undefined;
73 | }
74 | exports.findScriptPath = findScriptPath;
75 | function tapleafHash(leaf) {
76 | const version = leaf.version || exports.LEAF_VERSION_TAPSCRIPT;
77 | return bcrypto.taggedHash(
78 | 'TapLeaf',
79 | buffer_1.Buffer.concat([
80 | buffer_1.Buffer.from([version]),
81 | serializeScript(leaf.output),
82 | ]),
83 | );
84 | }
85 | exports.tapleafHash = tapleafHash;
86 | function tapTweakHash(pubKey, h) {
87 | return bcrypto.taggedHash(
88 | 'TapTweak',
89 | buffer_1.Buffer.concat(h ? [pubKey, h] : [pubKey]),
90 | );
91 | }
92 | exports.tapTweakHash = tapTweakHash;
93 | function tweakKey(pubKey, h) {
94 | if (!buffer_1.Buffer.isBuffer(pubKey)) return null;
95 | if (pubKey.length !== 32) return null;
96 | if (h && h.length !== 32) return null;
97 | const tweakHash = tapTweakHash(pubKey, h);
98 | const res = (0, ecc_lib_1.getEccLib)().xOnlyPointAddTweak(pubKey, tweakHash);
99 | if (!res || res.xOnlyPubkey === null) return null;
100 | return {
101 | parity: res.parity,
102 | x: buffer_1.Buffer.from(res.xOnlyPubkey),
103 | };
104 | }
105 | exports.tweakKey = tweakKey;
106 | function tapBranchHash(a, b) {
107 | return bcrypto.taggedHash('TapBranch', buffer_1.Buffer.concat([a, b]));
108 | }
109 | function serializeScript(s) {
110 | const varintLen = bufferutils_1.varuint.encodingLength(s.length);
111 | const buffer = buffer_1.Buffer.allocUnsafe(varintLen); // better
112 | bufferutils_1.varuint.encode(s.length, buffer);
113 | return buffer_1.Buffer.concat([buffer, s]);
114 | }
115 |
--------------------------------------------------------------------------------
/src/payments/embed.d.ts:
--------------------------------------------------------------------------------
1 | import { Payment, PaymentOpts } from './index';
2 | export declare function p2data(a: Payment, opts?: PaymentOpts): Payment;
3 |
--------------------------------------------------------------------------------
/src/payments/embed.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | Object.defineProperty(exports, '__esModule', { value: true });
3 | exports.p2data = void 0;
4 | const networks_1 = require('../networks');
5 | const bscript = require('../script');
6 | const types_1 = require('../types');
7 | const lazy = require('./lazy');
8 | const OPS = bscript.OPS;
9 | function stacksEqual(a, b) {
10 | if (a.length !== b.length) return false;
11 | return a.every((x, i) => {
12 | return x.equals(b[i]);
13 | });
14 | }
15 | // output: OP_RETURN ...
16 | function p2data(a, opts) {
17 | if (!a.data && !a.output) throw new TypeError('Not enough data');
18 | opts = Object.assign({ validate: true }, opts || {});
19 | (0, types_1.typeforce)(
20 | {
21 | network: types_1.typeforce.maybe(types_1.typeforce.Object),
22 | output: types_1.typeforce.maybe(types_1.typeforce.Buffer),
23 | data: types_1.typeforce.maybe(
24 | types_1.typeforce.arrayOf(types_1.typeforce.Buffer),
25 | ),
26 | },
27 | a,
28 | );
29 | const network = a.network || networks_1.bitcoin;
30 | const o = { name: 'embed', network };
31 | lazy.prop(o, 'output', () => {
32 | if (!a.data) return;
33 | return bscript.compile([OPS.OP_RETURN].concat(a.data));
34 | });
35 | lazy.prop(o, 'data', () => {
36 | if (!a.output) return;
37 | return bscript.decompile(a.output).slice(1);
38 | });
39 | // extended validation
40 | if (opts.validate) {
41 | if (a.output) {
42 | const chunks = bscript.decompile(a.output);
43 | if (chunks[0] !== OPS.OP_RETURN) throw new TypeError('Output is invalid');
44 | if (!chunks.slice(1).every(types_1.typeforce.Buffer))
45 | throw new TypeError('Output is invalid');
46 | if (a.data && !stacksEqual(a.data, o.data))
47 | throw new TypeError('Data mismatch');
48 | }
49 | }
50 | return Object.assign(o, a);
51 | }
52 | exports.p2data = p2data;
53 |
--------------------------------------------------------------------------------
/src/payments/index.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | import { Network } from '../networks';
3 | import { Taptree } from '../types';
4 | import { p2data as embed } from './embed';
5 | import { p2ms } from './p2ms';
6 | import { p2pk } from './p2pk';
7 | import { p2pkh } from './p2pkh';
8 | import { p2sh } from './p2sh';
9 | import { p2wpkh } from './p2wpkh';
10 | import { p2wsh } from './p2wsh';
11 | import { p2tr } from './p2tr';
12 | export interface Payment {
13 | name?: string;
14 | network?: Network;
15 | output?: Buffer;
16 | data?: Buffer[];
17 | m?: number;
18 | n?: number;
19 | pubkeys?: Buffer[];
20 | input?: Buffer;
21 | signatures?: Buffer[];
22 | internalPubkey?: Buffer;
23 | pubkey?: Buffer;
24 | signature?: Buffer;
25 | address?: string;
26 | hash?: Buffer;
27 | redeem?: Payment;
28 | redeemVersion?: number;
29 | scriptTree?: Taptree;
30 | witness?: Buffer[];
31 | }
32 | export type PaymentCreator = (a: Payment, opts?: PaymentOpts) => Payment;
33 | export type PaymentFunction = () => Payment;
34 | export interface PaymentOpts {
35 | validate?: boolean;
36 | allowIncomplete?: boolean;
37 | }
38 | export type StackElement = Buffer | number;
39 | export type Stack = StackElement[];
40 | export type StackFunction = () => Stack;
41 | export { embed, p2ms, p2pk, p2pkh, p2sh, p2wpkh, p2wsh, p2tr };
42 |
--------------------------------------------------------------------------------
/src/payments/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | Object.defineProperty(exports, '__esModule', { value: true });
3 | exports.p2tr =
4 | exports.p2wsh =
5 | exports.p2wpkh =
6 | exports.p2sh =
7 | exports.p2pkh =
8 | exports.p2pk =
9 | exports.p2ms =
10 | exports.embed =
11 | void 0;
12 | const embed_1 = require('./embed');
13 | Object.defineProperty(exports, 'embed', {
14 | enumerable: true,
15 | get: function () {
16 | return embed_1.p2data;
17 | },
18 | });
19 | const p2ms_1 = require('./p2ms');
20 | Object.defineProperty(exports, 'p2ms', {
21 | enumerable: true,
22 | get: function () {
23 | return p2ms_1.p2ms;
24 | },
25 | });
26 | const p2pk_1 = require('./p2pk');
27 | Object.defineProperty(exports, 'p2pk', {
28 | enumerable: true,
29 | get: function () {
30 | return p2pk_1.p2pk;
31 | },
32 | });
33 | const p2pkh_1 = require('./p2pkh');
34 | Object.defineProperty(exports, 'p2pkh', {
35 | enumerable: true,
36 | get: function () {
37 | return p2pkh_1.p2pkh;
38 | },
39 | });
40 | const p2sh_1 = require('./p2sh');
41 | Object.defineProperty(exports, 'p2sh', {
42 | enumerable: true,
43 | get: function () {
44 | return p2sh_1.p2sh;
45 | },
46 | });
47 | const p2wpkh_1 = require('./p2wpkh');
48 | Object.defineProperty(exports, 'p2wpkh', {
49 | enumerable: true,
50 | get: function () {
51 | return p2wpkh_1.p2wpkh;
52 | },
53 | });
54 | const p2wsh_1 = require('./p2wsh');
55 | Object.defineProperty(exports, 'p2wsh', {
56 | enumerable: true,
57 | get: function () {
58 | return p2wsh_1.p2wsh;
59 | },
60 | });
61 | const p2tr_1 = require('./p2tr');
62 | Object.defineProperty(exports, 'p2tr', {
63 | enumerable: true,
64 | get: function () {
65 | return p2tr_1.p2tr;
66 | },
67 | });
68 | // TODO
69 | // witness commitment
70 |
--------------------------------------------------------------------------------
/src/payments/lazy.d.ts:
--------------------------------------------------------------------------------
1 | export declare function prop(object: {}, name: string, f: () => any): void;
2 | export declare function value(f: () => T): () => T;
3 |
--------------------------------------------------------------------------------
/src/payments/lazy.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | Object.defineProperty(exports, '__esModule', { value: true });
3 | exports.value = exports.prop = void 0;
4 | function prop(object, name, f) {
5 | Object.defineProperty(object, name, {
6 | configurable: true,
7 | enumerable: true,
8 | get() {
9 | const _value = f.call(this);
10 | this[name] = _value;
11 | return _value;
12 | },
13 | set(_value) {
14 | Object.defineProperty(this, name, {
15 | configurable: true,
16 | enumerable: true,
17 | value: _value,
18 | writable: true,
19 | });
20 | },
21 | });
22 | }
23 | exports.prop = prop;
24 | function value(f) {
25 | let _value;
26 | return () => {
27 | if (_value !== undefined) return _value;
28 | _value = f();
29 | return _value;
30 | };
31 | }
32 | exports.value = value;
33 |
--------------------------------------------------------------------------------
/src/payments/p2ms.d.ts:
--------------------------------------------------------------------------------
1 | import { Payment, PaymentOpts } from './index';
2 | export declare function p2ms(a: Payment, opts?: PaymentOpts): Payment;
3 |
--------------------------------------------------------------------------------
/src/payments/p2ms.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | Object.defineProperty(exports, '__esModule', { value: true });
3 | exports.p2ms = void 0;
4 | const networks_1 = require('../networks');
5 | const bscript = require('../script');
6 | const types_1 = require('../types');
7 | const lazy = require('./lazy');
8 | const OPS = bscript.OPS;
9 | const OP_INT_BASE = OPS.OP_RESERVED; // OP_1 - 1
10 | function stacksEqual(a, b) {
11 | if (a.length !== b.length) return false;
12 | return a.every((x, i) => {
13 | return x.equals(b[i]);
14 | });
15 | }
16 | // input: OP_0 [signatures ...]
17 | // output: m [pubKeys ...] n OP_CHECKMULTISIG
18 | function p2ms(a, opts) {
19 | if (
20 | !a.input &&
21 | !a.output &&
22 | !(a.pubkeys && a.m !== undefined) &&
23 | !a.signatures
24 | )
25 | throw new TypeError('Not enough data');
26 | opts = Object.assign({ validate: true }, opts || {});
27 | function isAcceptableSignature(x) {
28 | return (
29 | bscript.isCanonicalScriptSignature(x) ||
30 | (opts.allowIncomplete && x === OPS.OP_0) !== undefined
31 | );
32 | }
33 | (0, types_1.typeforce)(
34 | {
35 | network: types_1.typeforce.maybe(types_1.typeforce.Object),
36 | m: types_1.typeforce.maybe(types_1.typeforce.Number),
37 | n: types_1.typeforce.maybe(types_1.typeforce.Number),
38 | output: types_1.typeforce.maybe(types_1.typeforce.Buffer),
39 | pubkeys: types_1.typeforce.maybe(
40 | types_1.typeforce.arrayOf(types_1.isPoint),
41 | ),
42 | signatures: types_1.typeforce.maybe(
43 | types_1.typeforce.arrayOf(isAcceptableSignature),
44 | ),
45 | input: types_1.typeforce.maybe(types_1.typeforce.Buffer),
46 | },
47 | a,
48 | );
49 | const network = a.network || networks_1.bitcoin;
50 | const o = { network };
51 | let chunks = [];
52 | let decoded = false;
53 | function decode(output) {
54 | if (decoded) return;
55 | decoded = true;
56 | chunks = bscript.decompile(output);
57 | o.m = chunks[0] - OP_INT_BASE;
58 | o.n = chunks[chunks.length - 2] - OP_INT_BASE;
59 | o.pubkeys = chunks.slice(1, -2);
60 | }
61 | lazy.prop(o, 'output', () => {
62 | if (!a.m) return;
63 | if (!o.n) return;
64 | if (!a.pubkeys) return;
65 | return bscript.compile(
66 | [].concat(
67 | OP_INT_BASE + a.m,
68 | a.pubkeys,
69 | OP_INT_BASE + o.n,
70 | OPS.OP_CHECKMULTISIG,
71 | ),
72 | );
73 | });
74 | lazy.prop(o, 'm', () => {
75 | if (!o.output) return;
76 | decode(o.output);
77 | return o.m;
78 | });
79 | lazy.prop(o, 'n', () => {
80 | if (!o.pubkeys) return;
81 | return o.pubkeys.length;
82 | });
83 | lazy.prop(o, 'pubkeys', () => {
84 | if (!a.output) return;
85 | decode(a.output);
86 | return o.pubkeys;
87 | });
88 | lazy.prop(o, 'signatures', () => {
89 | if (!a.input) return;
90 | return bscript.decompile(a.input).slice(1);
91 | });
92 | lazy.prop(o, 'input', () => {
93 | if (!a.signatures) return;
94 | return bscript.compile([OPS.OP_0].concat(a.signatures));
95 | });
96 | lazy.prop(o, 'witness', () => {
97 | if (!o.input) return;
98 | return [];
99 | });
100 | lazy.prop(o, 'name', () => {
101 | if (!o.m || !o.n) return;
102 | return `p2ms(${o.m} of ${o.n})`;
103 | });
104 | // extended validation
105 | if (opts.validate) {
106 | if (a.output) {
107 | decode(a.output);
108 | if (!types_1.typeforce.Number(chunks[0]))
109 | throw new TypeError('Output is invalid');
110 | if (!types_1.typeforce.Number(chunks[chunks.length - 2]))
111 | throw new TypeError('Output is invalid');
112 | if (chunks[chunks.length - 1] !== OPS.OP_CHECKMULTISIG)
113 | throw new TypeError('Output is invalid');
114 | if (o.m <= 0 || o.n > 16 || o.m > o.n || o.n !== chunks.length - 3)
115 | throw new TypeError('Output is invalid');
116 | if (!o.pubkeys.every(x => (0, types_1.isPoint)(x)))
117 | throw new TypeError('Output is invalid');
118 | if (a.m !== undefined && a.m !== o.m) throw new TypeError('m mismatch');
119 | if (a.n !== undefined && a.n !== o.n) throw new TypeError('n mismatch');
120 | if (a.pubkeys && !stacksEqual(a.pubkeys, o.pubkeys))
121 | throw new TypeError('Pubkeys mismatch');
122 | }
123 | if (a.pubkeys) {
124 | if (a.n !== undefined && a.n !== a.pubkeys.length)
125 | throw new TypeError('Pubkey count mismatch');
126 | o.n = a.pubkeys.length;
127 | if (o.n < o.m) throw new TypeError('Pubkey count cannot be less than m');
128 | }
129 | if (a.signatures) {
130 | if (a.signatures.length < o.m)
131 | throw new TypeError('Not enough signatures provided');
132 | if (a.signatures.length > o.m)
133 | throw new TypeError('Too many signatures provided');
134 | }
135 | if (a.input) {
136 | if (a.input[0] !== OPS.OP_0) throw new TypeError('Input is invalid');
137 | if (
138 | o.signatures.length === 0 ||
139 | !o.signatures.every(isAcceptableSignature)
140 | )
141 | throw new TypeError('Input has invalid signature(s)');
142 | if (a.signatures && !stacksEqual(a.signatures, o.signatures))
143 | throw new TypeError('Signature mismatch');
144 | if (a.m !== undefined && a.m !== a.signatures.length)
145 | throw new TypeError('Signature count mismatch');
146 | }
147 | }
148 | return Object.assign(o, a);
149 | }
150 | exports.p2ms = p2ms;
151 |
--------------------------------------------------------------------------------
/src/payments/p2pk.d.ts:
--------------------------------------------------------------------------------
1 | import { Payment, PaymentOpts } from './index';
2 | export declare function p2pk(a: Payment, opts?: PaymentOpts): Payment;
3 |
--------------------------------------------------------------------------------
/src/payments/p2pk.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | Object.defineProperty(exports, '__esModule', { value: true });
3 | exports.p2pk = void 0;
4 | const networks_1 = require('../networks');
5 | const bscript = require('../script');
6 | const types_1 = require('../types');
7 | const lazy = require('./lazy');
8 | const OPS = bscript.OPS;
9 | // input: {signature}
10 | // output: {pubKey} OP_CHECKSIG
11 | function p2pk(a, opts) {
12 | if (!a.input && !a.output && !a.pubkey && !a.input && !a.signature)
13 | throw new TypeError('Not enough data');
14 | opts = Object.assign({ validate: true }, opts || {});
15 | (0, types_1.typeforce)(
16 | {
17 | network: types_1.typeforce.maybe(types_1.typeforce.Object),
18 | output: types_1.typeforce.maybe(types_1.typeforce.Buffer),
19 | pubkey: types_1.typeforce.maybe(types_1.isPoint),
20 | signature: types_1.typeforce.maybe(bscript.isCanonicalScriptSignature),
21 | input: types_1.typeforce.maybe(types_1.typeforce.Buffer),
22 | },
23 | a,
24 | );
25 | const _chunks = lazy.value(() => {
26 | return bscript.decompile(a.input);
27 | });
28 | const network = a.network || networks_1.bitcoin;
29 | const o = { name: 'p2pk', network };
30 | lazy.prop(o, 'output', () => {
31 | if (!a.pubkey) return;
32 | return bscript.compile([a.pubkey, OPS.OP_CHECKSIG]);
33 | });
34 | lazy.prop(o, 'pubkey', () => {
35 | if (!a.output) return;
36 | return a.output.slice(1, -1);
37 | });
38 | lazy.prop(o, 'signature', () => {
39 | if (!a.input) return;
40 | return _chunks()[0];
41 | });
42 | lazy.prop(o, 'input', () => {
43 | if (!a.signature) return;
44 | return bscript.compile([a.signature]);
45 | });
46 | lazy.prop(o, 'witness', () => {
47 | if (!o.input) return;
48 | return [];
49 | });
50 | // extended validation
51 | if (opts.validate) {
52 | if (a.output) {
53 | if (a.output[a.output.length - 1] !== OPS.OP_CHECKSIG)
54 | throw new TypeError('Output is invalid');
55 | if (!(0, types_1.isPoint)(o.pubkey))
56 | throw new TypeError('Output pubkey is invalid');
57 | if (a.pubkey && !a.pubkey.equals(o.pubkey))
58 | throw new TypeError('Pubkey mismatch');
59 | }
60 | if (a.signature) {
61 | if (a.input && !a.input.equals(o.input))
62 | throw new TypeError('Signature mismatch');
63 | }
64 | if (a.input) {
65 | if (_chunks().length !== 1) throw new TypeError('Input is invalid');
66 | if (!bscript.isCanonicalScriptSignature(o.signature))
67 | throw new TypeError('Input has invalid signature');
68 | }
69 | }
70 | return Object.assign(o, a);
71 | }
72 | exports.p2pk = p2pk;
73 |
--------------------------------------------------------------------------------
/src/payments/p2pkh.d.ts:
--------------------------------------------------------------------------------
1 | import { Payment, PaymentOpts } from './index';
2 | export declare function p2pkh(a: Payment, opts?: PaymentOpts): Payment;
3 |
--------------------------------------------------------------------------------
/src/payments/p2pkh.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | Object.defineProperty(exports, '__esModule', { value: true });
3 | exports.p2pkh = void 0;
4 | const bcrypto = require('../crypto');
5 | const networks_1 = require('../networks');
6 | const bscript = require('../script');
7 | const types_1 = require('../types');
8 | const lazy = require('./lazy');
9 | const bs58check = require('bs58check');
10 | const OPS = bscript.OPS;
11 | // input: {signature} {pubkey}
12 | // output: OP_DUP OP_HASH160 {hash160(pubkey)} OP_EQUALVERIFY OP_CHECKSIG
13 | function p2pkh(a, opts) {
14 | if (!a.address && !a.hash && !a.output && !a.pubkey && !a.input)
15 | throw new TypeError('Not enough data');
16 | opts = Object.assign({ validate: true }, opts || {});
17 | (0, types_1.typeforce)(
18 | {
19 | network: types_1.typeforce.maybe(types_1.typeforce.Object),
20 | address: types_1.typeforce.maybe(types_1.typeforce.String),
21 | hash: types_1.typeforce.maybe(types_1.typeforce.BufferN(20)),
22 | output: types_1.typeforce.maybe(types_1.typeforce.BufferN(25)),
23 | pubkey: types_1.typeforce.maybe(types_1.isPoint),
24 | signature: types_1.typeforce.maybe(bscript.isCanonicalScriptSignature),
25 | input: types_1.typeforce.maybe(types_1.typeforce.Buffer),
26 | },
27 | a,
28 | );
29 | const _address = lazy.value(() => {
30 | const payload = Buffer.from(bs58check.decode(a.address));
31 | const version = payload.readUInt8(0);
32 | const hash = payload.slice(1);
33 | return { version, hash };
34 | });
35 | const _chunks = lazy.value(() => {
36 | return bscript.decompile(a.input);
37 | });
38 | const network = a.network || networks_1.bitcoin;
39 | const o = { name: 'p2pkh', network };
40 | lazy.prop(o, 'address', () => {
41 | if (!o.hash) return;
42 | const payload = Buffer.allocUnsafe(21);
43 | payload.writeUInt8(network.pubKeyHash, 0);
44 | o.hash.copy(payload, 1);
45 | return bs58check.encode(payload);
46 | });
47 | lazy.prop(o, 'hash', () => {
48 | if (a.output) return a.output.slice(3, 23);
49 | if (a.address) return _address().hash;
50 | if (a.pubkey || o.pubkey) return bcrypto.hash160(a.pubkey || o.pubkey);
51 | });
52 | lazy.prop(o, 'output', () => {
53 | if (!o.hash) return;
54 | return bscript.compile([
55 | OPS.OP_DUP,
56 | OPS.OP_HASH160,
57 | o.hash,
58 | OPS.OP_EQUALVERIFY,
59 | OPS.OP_CHECKSIG,
60 | ]);
61 | });
62 | lazy.prop(o, 'pubkey', () => {
63 | if (!a.input) return;
64 | return _chunks()[1];
65 | });
66 | lazy.prop(o, 'signature', () => {
67 | if (!a.input) return;
68 | return _chunks()[0];
69 | });
70 | lazy.prop(o, 'input', () => {
71 | if (!a.pubkey) return;
72 | if (!a.signature) return;
73 | return bscript.compile([a.signature, a.pubkey]);
74 | });
75 | lazy.prop(o, 'witness', () => {
76 | if (!o.input) return;
77 | return [];
78 | });
79 | // extended validation
80 | if (opts.validate) {
81 | let hash = Buffer.from([]);
82 | if (a.address) {
83 | if (_address().version !== network.pubKeyHash)
84 | throw new TypeError('Invalid version or Network mismatch');
85 | if (_address().hash.length !== 20) throw new TypeError('Invalid address');
86 | hash = _address().hash;
87 | }
88 | if (a.hash) {
89 | if (hash.length > 0 && !hash.equals(a.hash))
90 | throw new TypeError('Hash mismatch');
91 | else hash = a.hash;
92 | }
93 | if (a.output) {
94 | if (
95 | a.output.length !== 25 ||
96 | a.output[0] !== OPS.OP_DUP ||
97 | a.output[1] !== OPS.OP_HASH160 ||
98 | a.output[2] !== 0x14 ||
99 | a.output[23] !== OPS.OP_EQUALVERIFY ||
100 | a.output[24] !== OPS.OP_CHECKSIG
101 | )
102 | throw new TypeError('Output is invalid');
103 | const hash2 = a.output.slice(3, 23);
104 | if (hash.length > 0 && !hash.equals(hash2))
105 | throw new TypeError('Hash mismatch');
106 | else hash = hash2;
107 | }
108 | if (a.pubkey) {
109 | const pkh = bcrypto.hash160(a.pubkey);
110 | if (hash.length > 0 && !hash.equals(pkh))
111 | throw new TypeError('Hash mismatch');
112 | else hash = pkh;
113 | }
114 | if (a.input) {
115 | const chunks = _chunks();
116 | if (chunks.length !== 2) throw new TypeError('Input is invalid');
117 | if (!bscript.isCanonicalScriptSignature(chunks[0]))
118 | throw new TypeError('Input has invalid signature');
119 | if (!(0, types_1.isPoint)(chunks[1]))
120 | throw new TypeError('Input has invalid pubkey');
121 | if (a.signature && !a.signature.equals(chunks[0]))
122 | throw new TypeError('Signature mismatch');
123 | if (a.pubkey && !a.pubkey.equals(chunks[1]))
124 | throw new TypeError('Pubkey mismatch');
125 | const pkh = bcrypto.hash160(chunks[1]);
126 | if (hash.length > 0 && !hash.equals(pkh))
127 | throw new TypeError('Hash mismatch');
128 | }
129 | }
130 | return Object.assign(o, a);
131 | }
132 | exports.p2pkh = p2pkh;
133 |
--------------------------------------------------------------------------------
/src/payments/p2sh.d.ts:
--------------------------------------------------------------------------------
1 | import { Payment, PaymentOpts } from './index';
2 | export declare function p2sh(a: Payment, opts?: PaymentOpts): Payment;
3 |
--------------------------------------------------------------------------------
/src/payments/p2tr.d.ts:
--------------------------------------------------------------------------------
1 | import { Payment, PaymentOpts } from './index';
2 | export declare function p2tr(a: Payment, opts?: PaymentOpts): Payment;
3 |
--------------------------------------------------------------------------------
/src/payments/p2wpkh.d.ts:
--------------------------------------------------------------------------------
1 | import { Payment, PaymentOpts } from './index';
2 | export declare function p2wpkh(a: Payment, opts?: PaymentOpts): Payment;
3 |
--------------------------------------------------------------------------------
/src/payments/p2wpkh.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | Object.defineProperty(exports, '__esModule', { value: true });
3 | exports.p2wpkh = void 0;
4 | const bcrypto = require('../crypto');
5 | const networks_1 = require('../networks');
6 | const bscript = require('../script');
7 | const types_1 = require('../types');
8 | const lazy = require('./lazy');
9 | const bech32_1 = require('bech32');
10 | const OPS = bscript.OPS;
11 | const EMPTY_BUFFER = Buffer.alloc(0);
12 | // witness: {signature} {pubKey}
13 | // input: <>
14 | // output: OP_0 {pubKeyHash}
15 | function p2wpkh(a, opts) {
16 | if (!a.address && !a.hash && !a.output && !a.pubkey && !a.witness)
17 | throw new TypeError('Not enough data');
18 | opts = Object.assign({ validate: true }, opts || {});
19 | (0, types_1.typeforce)(
20 | {
21 | address: types_1.typeforce.maybe(types_1.typeforce.String),
22 | hash: types_1.typeforce.maybe(types_1.typeforce.BufferN(20)),
23 | input: types_1.typeforce.maybe(types_1.typeforce.BufferN(0)),
24 | network: types_1.typeforce.maybe(types_1.typeforce.Object),
25 | output: types_1.typeforce.maybe(types_1.typeforce.BufferN(22)),
26 | pubkey: types_1.typeforce.maybe(types_1.isPoint),
27 | signature: types_1.typeforce.maybe(bscript.isCanonicalScriptSignature),
28 | witness: types_1.typeforce.maybe(
29 | types_1.typeforce.arrayOf(types_1.typeforce.Buffer),
30 | ),
31 | },
32 | a,
33 | );
34 | const _address = lazy.value(() => {
35 | const result = bech32_1.bech32.decode(a.address);
36 | const version = result.words.shift();
37 | const data = bech32_1.bech32.fromWords(result.words);
38 | return {
39 | version,
40 | prefix: result.prefix,
41 | data: Buffer.from(data),
42 | };
43 | });
44 | const network = a.network || networks_1.bitcoin;
45 | const o = { name: 'p2wpkh', network };
46 | lazy.prop(o, 'address', () => {
47 | if (!o.hash) return;
48 | const words = bech32_1.bech32.toWords(o.hash);
49 | words.unshift(0x00);
50 | return bech32_1.bech32.encode(network.bech32, words);
51 | });
52 | lazy.prop(o, 'hash', () => {
53 | if (a.output) return a.output.slice(2, 22);
54 | if (a.address) return _address().data;
55 | if (a.pubkey || o.pubkey) return bcrypto.hash160(a.pubkey || o.pubkey);
56 | });
57 | lazy.prop(o, 'output', () => {
58 | if (!o.hash) return;
59 | return bscript.compile([OPS.OP_0, o.hash]);
60 | });
61 | lazy.prop(o, 'pubkey', () => {
62 | if (a.pubkey) return a.pubkey;
63 | if (!a.witness) return;
64 | return a.witness[1];
65 | });
66 | lazy.prop(o, 'signature', () => {
67 | if (!a.witness) return;
68 | return a.witness[0];
69 | });
70 | lazy.prop(o, 'input', () => {
71 | if (!o.witness) return;
72 | return EMPTY_BUFFER;
73 | });
74 | lazy.prop(o, 'witness', () => {
75 | if (!a.pubkey) return;
76 | if (!a.signature) return;
77 | return [a.signature, a.pubkey];
78 | });
79 | // extended validation
80 | if (opts.validate) {
81 | let hash = Buffer.from([]);
82 | if (a.address) {
83 | if (network && network.bech32 !== _address().prefix)
84 | throw new TypeError('Invalid prefix or Network mismatch');
85 | if (_address().version !== 0x00)
86 | throw new TypeError('Invalid address version');
87 | if (_address().data.length !== 20)
88 | throw new TypeError('Invalid address data');
89 | hash = _address().data;
90 | }
91 | if (a.hash) {
92 | if (hash.length > 0 && !hash.equals(a.hash))
93 | throw new TypeError('Hash mismatch');
94 | else hash = a.hash;
95 | }
96 | if (a.output) {
97 | if (
98 | a.output.length !== 22 ||
99 | a.output[0] !== OPS.OP_0 ||
100 | a.output[1] !== 0x14
101 | )
102 | throw new TypeError('Output is invalid');
103 | if (hash.length > 0 && !hash.equals(a.output.slice(2)))
104 | throw new TypeError('Hash mismatch');
105 | else hash = a.output.slice(2);
106 | }
107 | if (a.pubkey) {
108 | const pkh = bcrypto.hash160(a.pubkey);
109 | if (hash.length > 0 && !hash.equals(pkh))
110 | throw new TypeError('Hash mismatch');
111 | else hash = pkh;
112 | if (!(0, types_1.isPoint)(a.pubkey) || a.pubkey.length !== 33)
113 | throw new TypeError('Invalid pubkey for p2wpkh');
114 | }
115 | if (a.witness) {
116 | if (a.witness.length !== 2) throw new TypeError('Witness is invalid');
117 | if (!bscript.isCanonicalScriptSignature(a.witness[0]))
118 | throw new TypeError('Witness has invalid signature');
119 | if (!(0, types_1.isPoint)(a.witness[1]) || a.witness[1].length !== 33)
120 | throw new TypeError('Witness has invalid pubkey');
121 | if (a.signature && !a.signature.equals(a.witness[0]))
122 | throw new TypeError('Signature mismatch');
123 | if (a.pubkey && !a.pubkey.equals(a.witness[1]))
124 | throw new TypeError('Pubkey mismatch');
125 | const pkh = bcrypto.hash160(a.witness[1]);
126 | if (hash.length > 0 && !hash.equals(pkh))
127 | throw new TypeError('Hash mismatch');
128 | }
129 | }
130 | return Object.assign(o, a);
131 | }
132 | exports.p2wpkh = p2wpkh;
133 |
--------------------------------------------------------------------------------
/src/payments/p2wsh.d.ts:
--------------------------------------------------------------------------------
1 | import { Payment, PaymentOpts } from './index';
2 | export declare function p2wsh(a: Payment, opts?: PaymentOpts): Payment;
3 |
--------------------------------------------------------------------------------
/src/psbt/bip371.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | import { Taptree } from '../types';
3 | import { PsbtInput, PsbtOutput, TapLeaf } from 'bip174/src/lib/interfaces';
4 | export declare const toXOnly: (pubKey: Buffer) => Buffer;
5 | /**
6 | * Default tapscript finalizer. It searches for the `tapLeafHashToFinalize` if provided.
7 | * Otherwise it will search for the tapleaf that has at least one signature and has the shortest path.
8 | * @param inputIndex the position of the PSBT input.
9 | * @param input the PSBT input.
10 | * @param tapLeafHashToFinalize optional, if provided the finalizer will search for a tapleaf that has this hash
11 | * and will try to build the finalScriptWitness.
12 | * @returns the finalScriptWitness or throws an exception if no tapleaf found.
13 | */
14 | export declare function tapScriptFinalizer(inputIndex: number, input: PsbtInput, tapLeafHashToFinalize?: Buffer): {
15 | finalScriptWitness: Buffer | undefined;
16 | };
17 | export declare function serializeTaprootSignature(sig: Buffer, sighashType?: number): Buffer;
18 | export declare function isTaprootInput(input: PsbtInput): boolean;
19 | export declare function isTaprootOutput(output: PsbtOutput, script?: Buffer): boolean;
20 | export declare function checkTaprootInputFields(inputData: PsbtInput, newInputData: PsbtInput, action: string): void;
21 | export declare function checkTaprootOutputFields(outputData: PsbtOutput, newOutputData: PsbtOutput, action: string): void;
22 | export declare function tweakInternalPubKey(inputIndex: number, input: PsbtInput): Buffer;
23 | /**
24 | * Convert a binary tree to a BIP371 type list. Each element of the list is (according to BIP371):
25 | * One or more tuples representing the depth, leaf version, and script for a leaf in the Taproot tree,
26 | * allowing the entire tree to be reconstructed. The tuples must be in depth first search order so that
27 | * the tree is correctly reconstructed.
28 | * @param tree the binary tap tree
29 | * @returns a list of BIP 371 tapleaves
30 | */
31 | export declare function tapTreeToList(tree: Taptree): TapLeaf[];
32 | /**
33 | * Convert a BIP371 TapLeaf list to a TapTree (binary).
34 | * @param leaves a list of tapleaves where each element of the list is (according to BIP371):
35 | * One or more tuples representing the depth, leaf version, and script for a leaf in the Taproot tree,
36 | * allowing the entire tree to be reconstructed. The tuples must be in depth first search order so that
37 | * the tree is correctly reconstructed.
38 | * @returns the corresponding taptree, or throws an exception if the tree cannot be reconstructed
39 | */
40 | export declare function tapTreeFromList(leaves?: TapLeaf[]): Taptree;
41 | export declare function checkTaprootInputForSigs(input: PsbtInput, action: string): boolean;
42 |
--------------------------------------------------------------------------------
/src/psbt/psbtutils.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | import { PsbtInput } from 'bip174/src/lib/interfaces';
3 | export declare const isP2MS: (script: Buffer) => boolean;
4 | export declare const isP2PK: (script: Buffer) => boolean;
5 | export declare const isP2PKH: (script: Buffer) => boolean;
6 | export declare const isP2WPKH: (script: Buffer) => boolean;
7 | export declare const isP2WSHScript: (script: Buffer) => boolean;
8 | export declare const isP2SHScript: (script: Buffer) => boolean;
9 | export declare const isP2TR: (script: Buffer) => boolean;
10 | export declare function witnessStackToScriptWitness(witness: Buffer[]): Buffer;
11 | export declare function pubkeyPositionInScript(pubkey: Buffer, script: Buffer): number;
12 | export declare function pubkeyInScript(pubkey: Buffer, script: Buffer): boolean;
13 | export declare function checkInputForSig(input: PsbtInput, action: string): boolean;
14 | type SignatureDecodeFunc = (buffer: Buffer) => {
15 | signature: Buffer;
16 | hashType: number;
17 | };
18 | export declare function signatureBlocksAction(signature: Buffer, signatureDecodeFn: SignatureDecodeFunc, action: string): boolean;
19 | export {};
20 |
--------------------------------------------------------------------------------
/src/psbt/psbtutils.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | Object.defineProperty(exports, '__esModule', { value: true });
3 | exports.signatureBlocksAction =
4 | exports.checkInputForSig =
5 | exports.pubkeyInScript =
6 | exports.pubkeyPositionInScript =
7 | exports.witnessStackToScriptWitness =
8 | exports.isP2TR =
9 | exports.isP2SHScript =
10 | exports.isP2WSHScript =
11 | exports.isP2WPKH =
12 | exports.isP2PKH =
13 | exports.isP2PK =
14 | exports.isP2MS =
15 | void 0;
16 | const varuint = require('bip174/src/lib/converter/varint');
17 | const bscript = require('../script');
18 | const transaction_1 = require('../transaction');
19 | const crypto_1 = require('../crypto');
20 | const payments = require('../payments');
21 | function isPaymentFactory(payment) {
22 | return script => {
23 | try {
24 | payment({ output: script });
25 | return true;
26 | } catch (err) {
27 | return false;
28 | }
29 | };
30 | }
31 | exports.isP2MS = isPaymentFactory(payments.p2ms);
32 | exports.isP2PK = isPaymentFactory(payments.p2pk);
33 | exports.isP2PKH = isPaymentFactory(payments.p2pkh);
34 | exports.isP2WPKH = isPaymentFactory(payments.p2wpkh);
35 | exports.isP2WSHScript = isPaymentFactory(payments.p2wsh);
36 | exports.isP2SHScript = isPaymentFactory(payments.p2sh);
37 | exports.isP2TR = isPaymentFactory(payments.p2tr);
38 | function witnessStackToScriptWitness(witness) {
39 | let buffer = Buffer.allocUnsafe(0);
40 | function writeSlice(slice) {
41 | buffer = Buffer.concat([buffer, Buffer.from(slice)]);
42 | }
43 | function writeVarInt(i) {
44 | const currentLen = buffer.length;
45 | const varintLen = varuint.encodingLength(i);
46 | buffer = Buffer.concat([buffer, Buffer.allocUnsafe(varintLen)]);
47 | varuint.encode(i, buffer, currentLen);
48 | }
49 | function writeVarSlice(slice) {
50 | writeVarInt(slice.length);
51 | writeSlice(slice);
52 | }
53 | function writeVector(vector) {
54 | writeVarInt(vector.length);
55 | vector.forEach(writeVarSlice);
56 | }
57 | writeVector(witness);
58 | return buffer;
59 | }
60 | exports.witnessStackToScriptWitness = witnessStackToScriptWitness;
61 | function pubkeyPositionInScript(pubkey, script) {
62 | const pubkeyHash = (0, crypto_1.hash160)(pubkey);
63 | const pubkeyXOnly = pubkey.slice(1, 33); // slice before calling?
64 | const decompiled = bscript.decompile(script);
65 | if (decompiled === null) throw new Error('Unknown script error');
66 | return decompiled.findIndex(element => {
67 | if (typeof element === 'number') return false;
68 | return (
69 | element.equals(pubkey) ||
70 | element.equals(pubkeyHash) ||
71 | element.equals(pubkeyXOnly)
72 | );
73 | });
74 | }
75 | exports.pubkeyPositionInScript = pubkeyPositionInScript;
76 | function pubkeyInScript(pubkey, script) {
77 | return pubkeyPositionInScript(pubkey, script) !== -1;
78 | }
79 | exports.pubkeyInScript = pubkeyInScript;
80 | function checkInputForSig(input, action) {
81 | const pSigs = extractPartialSigs(input);
82 | return pSigs.some(pSig =>
83 | signatureBlocksAction(pSig, bscript.signature.decode, action),
84 | );
85 | }
86 | exports.checkInputForSig = checkInputForSig;
87 | function signatureBlocksAction(signature, signatureDecodeFn, action) {
88 | const { hashType } = signatureDecodeFn(signature);
89 | const whitelist = [];
90 | const isAnyoneCanPay =
91 | hashType & transaction_1.Transaction.SIGHASH_ANYONECANPAY;
92 | if (isAnyoneCanPay) whitelist.push('addInput');
93 | const hashMod = hashType & 0x1f;
94 | switch (hashMod) {
95 | case transaction_1.Transaction.SIGHASH_ALL:
96 | break;
97 | case transaction_1.Transaction.SIGHASH_SINGLE:
98 | case transaction_1.Transaction.SIGHASH_NONE:
99 | whitelist.push('addOutput');
100 | whitelist.push('setInputSequence');
101 | break;
102 | }
103 | if (whitelist.indexOf(action) === -1) {
104 | return true;
105 | }
106 | return false;
107 | }
108 | exports.signatureBlocksAction = signatureBlocksAction;
109 | function extractPartialSigs(input) {
110 | let pSigs = [];
111 | if ((input.partialSig || []).length === 0) {
112 | if (!input.finalScriptSig && !input.finalScriptWitness) return [];
113 | pSigs = getPsigsFromInputFinalScripts(input);
114 | } else {
115 | pSigs = input.partialSig;
116 | }
117 | return pSigs.map(p => p.signature);
118 | }
119 | function getPsigsFromInputFinalScripts(input) {
120 | const scriptItems = !input.finalScriptSig
121 | ? []
122 | : bscript.decompile(input.finalScriptSig) || [];
123 | const witnessItems = !input.finalScriptWitness
124 | ? []
125 | : bscript.decompile(input.finalScriptWitness) || [];
126 | return scriptItems
127 | .concat(witnessItems)
128 | .filter(item => {
129 | return Buffer.isBuffer(item) && bscript.isCanonicalScriptSignature(item);
130 | })
131 | .map(sig => ({ signature: sig }));
132 | }
133 |
--------------------------------------------------------------------------------
/src/push_data.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | export declare function encodingLength(i: number): number;
3 | export declare function encode(buffer: Buffer, num: number, offset: number): number;
4 | export declare function decode(buffer: Buffer, offset: number): {
5 | opcode: number;
6 | number: number;
7 | size: number;
8 | } | null;
9 |
--------------------------------------------------------------------------------
/src/push_data.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | Object.defineProperty(exports, '__esModule', { value: true });
3 | exports.decode = exports.encode = exports.encodingLength = void 0;
4 | const ops_1 = require('./ops');
5 | function encodingLength(i) {
6 | return i < ops_1.OPS.OP_PUSHDATA1 ? 1 : i <= 0xff ? 2 : i <= 0xffff ? 3 : 5;
7 | }
8 | exports.encodingLength = encodingLength;
9 | function encode(buffer, num, offset) {
10 | const size = encodingLength(num);
11 | // ~6 bit
12 | if (size === 1) {
13 | buffer.writeUInt8(num, offset);
14 | // 8 bit
15 | } else if (size === 2) {
16 | buffer.writeUInt8(ops_1.OPS.OP_PUSHDATA1, offset);
17 | buffer.writeUInt8(num, offset + 1);
18 | // 16 bit
19 | } else if (size === 3) {
20 | buffer.writeUInt8(ops_1.OPS.OP_PUSHDATA2, offset);
21 | buffer.writeUInt16LE(num, offset + 1);
22 | // 32 bit
23 | } else {
24 | buffer.writeUInt8(ops_1.OPS.OP_PUSHDATA4, offset);
25 | buffer.writeUInt32LE(num, offset + 1);
26 | }
27 | return size;
28 | }
29 | exports.encode = encode;
30 | function decode(buffer, offset) {
31 | const opcode = buffer.readUInt8(offset);
32 | let num;
33 | let size;
34 | // ~6 bit
35 | if (opcode < ops_1.OPS.OP_PUSHDATA1) {
36 | num = opcode;
37 | size = 1;
38 | // 8 bit
39 | } else if (opcode === ops_1.OPS.OP_PUSHDATA1) {
40 | if (offset + 2 > buffer.length) return null;
41 | num = buffer.readUInt8(offset + 1);
42 | size = 2;
43 | // 16 bit
44 | } else if (opcode === ops_1.OPS.OP_PUSHDATA2) {
45 | if (offset + 3 > buffer.length) return null;
46 | num = buffer.readUInt16LE(offset + 1);
47 | size = 3;
48 | // 32 bit
49 | } else {
50 | if (offset + 5 > buffer.length) return null;
51 | if (opcode !== ops_1.OPS.OP_PUSHDATA4) throw new Error('Unexpected opcode');
52 | num = buffer.readUInt32LE(offset + 1);
53 | size = 5;
54 | }
55 | return {
56 | opcode,
57 | number: num,
58 | size,
59 | };
60 | }
61 | exports.decode = decode;
62 |
--------------------------------------------------------------------------------
/src/script.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | import { OPS } from './ops';
3 | import { Stack } from './payments';
4 | import * as scriptNumber from './script_number';
5 | import * as scriptSignature from './script_signature';
6 | export { OPS };
7 | export declare function isPushOnly(value: Stack): boolean;
8 | export declare function countNonPushOnlyOPs(value: Stack): number;
9 | export declare function compile(chunks: Buffer | Stack): Buffer;
10 | export declare function decompile(buffer: Buffer | Array): Array | null;
11 | export declare function toASM(chunks: Buffer | Array): string;
12 | export declare function fromASM(asm: string): Buffer;
13 | export declare function toStack(chunks: Buffer | Array): Buffer[];
14 | export declare function isCanonicalPubKey(buffer: Buffer): boolean;
15 | export declare function isDefinedHashType(hashType: number): boolean;
16 | export declare function isCanonicalScriptSignature(buffer: Buffer): boolean;
17 | export declare const number: typeof scriptNumber;
18 | export declare const signature: typeof scriptSignature;
19 |
--------------------------------------------------------------------------------
/src/script_number.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | export declare function decode(buffer: Buffer, maxLength?: number, minimal?: boolean): number;
3 | export declare function encode(_number: number): Buffer;
4 |
--------------------------------------------------------------------------------
/src/script_number.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | Object.defineProperty(exports, '__esModule', { value: true });
3 | exports.encode = exports.decode = void 0;
4 | function decode(buffer, maxLength, minimal) {
5 | maxLength = maxLength || 4;
6 | minimal = minimal === undefined ? true : minimal;
7 | const length = buffer.length;
8 | if (length === 0) return 0;
9 | if (length > maxLength) throw new TypeError('Script number overflow');
10 | if (minimal) {
11 | if ((buffer[length - 1] & 0x7f) === 0) {
12 | if (length <= 1 || (buffer[length - 2] & 0x80) === 0)
13 | throw new Error('Non-minimally encoded script number');
14 | }
15 | }
16 | // 40-bit
17 | if (length === 5) {
18 | const a = buffer.readUInt32LE(0);
19 | const b = buffer.readUInt8(4);
20 | if (b & 0x80) return -((b & ~0x80) * 0x100000000 + a);
21 | return b * 0x100000000 + a;
22 | }
23 | // 32-bit / 24-bit / 16-bit / 8-bit
24 | let result = 0;
25 | for (let i = 0; i < length; ++i) {
26 | result |= buffer[i] << (8 * i);
27 | }
28 | if (buffer[length - 1] & 0x80)
29 | return -(result & ~(0x80 << (8 * (length - 1))));
30 | return result;
31 | }
32 | exports.decode = decode;
33 | function scriptNumSize(i) {
34 | return i > 0x7fffffff
35 | ? 5
36 | : i > 0x7fffff
37 | ? 4
38 | : i > 0x7fff
39 | ? 3
40 | : i > 0x7f
41 | ? 2
42 | : i > 0x00
43 | ? 1
44 | : 0;
45 | }
46 | function encode(_number) {
47 | let value = Math.abs(_number);
48 | const size = scriptNumSize(value);
49 | const buffer = Buffer.allocUnsafe(size);
50 | const negative = _number < 0;
51 | for (let i = 0; i < size; ++i) {
52 | buffer.writeUInt8(value & 0xff, i);
53 | value >>= 8;
54 | }
55 | if (buffer[size - 1] & 0x80) {
56 | buffer.writeUInt8(negative ? 0x80 : 0x00, size - 1);
57 | } else if (negative) {
58 | buffer[size - 1] |= 0x80;
59 | }
60 | return buffer;
61 | }
62 | exports.encode = encode;
63 |
--------------------------------------------------------------------------------
/src/script_signature.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | interface ScriptSignature {
3 | signature: Buffer;
4 | hashType: number;
5 | }
6 | export declare function decode(buffer: Buffer): ScriptSignature;
7 | export declare function encode(signature: Buffer, hashType: number): Buffer;
8 | export {};
9 |
--------------------------------------------------------------------------------
/src/script_signature.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | Object.defineProperty(exports, '__esModule', { value: true });
3 | exports.encode = exports.decode = void 0;
4 | const bip66 = require('./bip66');
5 | const types = require('./types');
6 | const { typeforce } = types;
7 | const ZERO = Buffer.alloc(1, 0);
8 | function toDER(x) {
9 | let i = 0;
10 | while (x[i] === 0) ++i;
11 | if (i === x.length) return ZERO;
12 | x = x.slice(i);
13 | if (x[0] & 0x80) return Buffer.concat([ZERO, x], 1 + x.length);
14 | return x;
15 | }
16 | function fromDER(x) {
17 | if (x[0] === 0x00) x = x.slice(1);
18 | const buffer = Buffer.alloc(32, 0);
19 | const bstart = Math.max(0, 32 - x.length);
20 | x.copy(buffer, bstart);
21 | return buffer;
22 | }
23 | // BIP62: 1 byte hashType flag (only 0x01, 0x02, 0x03, 0x81, 0x82 and 0x83 are allowed)
24 | function decode(buffer) {
25 | const hashType = buffer.readUInt8(buffer.length - 1);
26 | const hashTypeMod = hashType & ~0x80;
27 | if (hashTypeMod <= 0 || hashTypeMod >= 4)
28 | throw new Error('Invalid hashType ' + hashType);
29 | const decoded = bip66.decode(buffer.slice(0, -1));
30 | const r = fromDER(decoded.r);
31 | const s = fromDER(decoded.s);
32 | const signature = Buffer.concat([r, s], 64);
33 | return { signature, hashType };
34 | }
35 | exports.decode = decode;
36 | function encode(signature, hashType) {
37 | typeforce(
38 | {
39 | signature: types.BufferN(64),
40 | hashType: types.UInt8,
41 | },
42 | { signature, hashType },
43 | );
44 | const hashTypeMod = hashType & ~0x80;
45 | if (hashTypeMod <= 0 || hashTypeMod >= 4)
46 | throw new Error('Invalid hashType ' + hashType);
47 | const hashTypeBuffer = Buffer.allocUnsafe(1);
48 | hashTypeBuffer.writeUInt8(hashType, 0);
49 | const r = toDER(signature.slice(0, 32));
50 | const s = toDER(signature.slice(32, 64));
51 | return Buffer.concat([bip66.encode(r, s), hashTypeBuffer]);
52 | }
53 | exports.encode = encode;
54 |
--------------------------------------------------------------------------------
/src/transaction.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | export interface Output {
3 | script: Buffer;
4 | value: number;
5 | }
6 | export interface Input {
7 | hash: Buffer;
8 | index: number;
9 | script: Buffer;
10 | sequence: number;
11 | witness: Buffer[];
12 | }
13 | export declare class Transaction {
14 | static readonly DEFAULT_SEQUENCE = 4294967295;
15 | static readonly SIGHASH_DEFAULT = 0;
16 | static readonly SIGHASH_ALL = 1;
17 | static readonly SIGHASH_NONE = 2;
18 | static readonly SIGHASH_SINGLE = 3;
19 | static readonly SIGHASH_ANYONECANPAY = 128;
20 | static readonly SIGHASH_OUTPUT_MASK = 3;
21 | static readonly SIGHASH_INPUT_MASK = 128;
22 | static readonly ADVANCED_TRANSACTION_MARKER = 0;
23 | static readonly ADVANCED_TRANSACTION_FLAG = 1;
24 | static fromBuffer(buffer: Buffer, _NO_STRICT?: boolean): Transaction;
25 | static fromHex(hex: string): Transaction;
26 | static isCoinbaseHash(buffer: Buffer): boolean;
27 | version: number;
28 | locktime: number;
29 | ins: Input[];
30 | outs: Output[];
31 | isCoinbase(): boolean;
32 | addInput(hash: Buffer, index: number, sequence?: number, scriptSig?: Buffer): number;
33 | addOutput(scriptPubKey: Buffer, value: number): number;
34 | hasWitnesses(): boolean;
35 | weight(): number;
36 | virtualSize(): number;
37 | byteLength(_ALLOW_WITNESS?: boolean): number;
38 | clone(): Transaction;
39 | /**
40 | * Hash transaction for signing a specific input.
41 | *
42 | * Bitcoin uses a different hash for each signed transaction input.
43 | * This method copies the transaction, makes the necessary changes based on the
44 | * hashType, and then hashes the result.
45 | * This hash can then be used to sign the provided transaction input.
46 | */
47 | hashForSignature(inIndex: number, prevOutScript: Buffer, hashType: number): Buffer;
48 | hashForWitnessV1(inIndex: number, prevOutScripts: Buffer[], values: number[], hashType: number, leafHash?: Buffer, annex?: Buffer): Buffer;
49 | hashForWitnessV0(inIndex: number, prevOutScript: Buffer, value: number, hashType: number): Buffer;
50 | getHash(forWitness?: boolean): Buffer;
51 | getId(): string;
52 | toBuffer(buffer?: Buffer, initialOffset?: number): Buffer;
53 | toHex(): string;
54 | setInputScript(index: number, scriptSig: Buffer): void;
55 | setWitness(index: number, witness: Buffer[]): void;
56 | private __toBuffer;
57 | }
58 |
--------------------------------------------------------------------------------
/src/types.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | export declare const typeforce: any;
3 | export declare function isPoint(p: Buffer | number | undefined | null): boolean;
4 | export declare function UInt31(value: number): boolean;
5 | export declare function BIP32Path(value: string): boolean;
6 | export declare namespace BIP32Path {
7 | var toJSON: () => string;
8 | }
9 | export declare function Signer(obj: any): boolean;
10 | export declare function Satoshi(value: number): boolean;
11 | export declare const ECPoint: any;
12 | export declare const Network: any;
13 | export interface XOnlyPointAddTweakResult {
14 | parity: 1 | 0;
15 | xOnlyPubkey: Uint8Array;
16 | }
17 | export interface Tapleaf {
18 | output: Buffer;
19 | version?: number;
20 | }
21 | export declare const TAPLEAF_VERSION_MASK = 254;
22 | export declare function isTapleaf(o: any): o is Tapleaf;
23 | /**
24 | * Binary tree repsenting script path spends for a Taproot input.
25 | * Each node is either a single Tapleaf, or a pair of Tapleaf | Taptree.
26 | * The tree has no balancing requirements.
27 | */
28 | export type Taptree = [Taptree | Tapleaf, Taptree | Tapleaf] | Tapleaf;
29 | export declare function isTaptree(scriptTree: any): scriptTree is Taptree;
30 | export interface TinySecp256k1Interface {
31 | isXOnlyPoint(p: Uint8Array): boolean;
32 | xOnlyPointAddTweak(p: Uint8Array, tweak: Uint8Array): XOnlyPointAddTweakResult | null;
33 | }
34 | export declare const Buffer256bit: any;
35 | export declare const Hash160bit: any;
36 | export declare const Hash256bit: any;
37 | export declare const Number: any;
38 | export declare const Array: any;
39 | export declare const Boolean: any;
40 | export declare const String: any;
41 | export declare const Buffer: any;
42 | export declare const Hex: any;
43 | export declare const maybe: any;
44 | export declare const tuple: any;
45 | export declare const UInt8: any;
46 | export declare const UInt32: any;
47 | export declare const Function: any;
48 | export declare const BufferN: any;
49 | export declare const Null: any;
50 | export declare const oneOf: any;
51 |
--------------------------------------------------------------------------------
/src/types.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | Object.defineProperty(exports, '__esModule', { value: true });
3 | exports.oneOf =
4 | exports.Null =
5 | exports.BufferN =
6 | exports.Function =
7 | exports.UInt32 =
8 | exports.UInt8 =
9 | exports.tuple =
10 | exports.maybe =
11 | exports.Hex =
12 | exports.Buffer =
13 | exports.String =
14 | exports.Boolean =
15 | exports.Array =
16 | exports.Number =
17 | exports.Hash256bit =
18 | exports.Hash160bit =
19 | exports.Buffer256bit =
20 | exports.isTaptree =
21 | exports.isTapleaf =
22 | exports.TAPLEAF_VERSION_MASK =
23 | exports.Network =
24 | exports.ECPoint =
25 | exports.Satoshi =
26 | exports.Signer =
27 | exports.BIP32Path =
28 | exports.UInt31 =
29 | exports.isPoint =
30 | exports.typeforce =
31 | void 0;
32 | const buffer_1 = require('buffer');
33 | exports.typeforce = require('typeforce');
34 | const ZERO32 = buffer_1.Buffer.alloc(32, 0);
35 | const EC_P = buffer_1.Buffer.from(
36 | 'fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f',
37 | 'hex',
38 | );
39 | function isPoint(p) {
40 | if (!buffer_1.Buffer.isBuffer(p)) return false;
41 | if (p.length < 33) return false;
42 | const t = p[0];
43 | const x = p.slice(1, 33);
44 | if (x.compare(ZERO32) === 0) return false;
45 | if (x.compare(EC_P) >= 0) return false;
46 | if ((t === 0x02 || t === 0x03) && p.length === 33) {
47 | return true;
48 | }
49 | const y = p.slice(33);
50 | if (y.compare(ZERO32) === 0) return false;
51 | if (y.compare(EC_P) >= 0) return false;
52 | if (t === 0x04 && p.length === 65) return true;
53 | return false;
54 | }
55 | exports.isPoint = isPoint;
56 | const UINT31_MAX = Math.pow(2, 31) - 1;
57 | function UInt31(value) {
58 | return exports.typeforce.UInt32(value) && value <= UINT31_MAX;
59 | }
60 | exports.UInt31 = UInt31;
61 | function BIP32Path(value) {
62 | return (
63 | exports.typeforce.String(value) && !!value.match(/^(m\/)?(\d+'?\/)*\d+'?$/)
64 | );
65 | }
66 | exports.BIP32Path = BIP32Path;
67 | BIP32Path.toJSON = () => {
68 | return 'BIP32 derivation path';
69 | };
70 | function Signer(obj) {
71 | return (
72 | (exports.typeforce.Buffer(obj.publicKey) ||
73 | typeof obj.getPublicKey === 'function') &&
74 | typeof obj.sign === 'function'
75 | );
76 | }
77 | exports.Signer = Signer;
78 | const SATOSHI_MAX = 21 * 1e14;
79 | function Satoshi(value) {
80 | return exports.typeforce.UInt53(value) && value <= SATOSHI_MAX;
81 | }
82 | exports.Satoshi = Satoshi;
83 | // external dependent types
84 | exports.ECPoint = exports.typeforce.quacksLike('Point');
85 | // exposed, external API
86 | exports.Network = exports.typeforce.compile({
87 | messagePrefix: exports.typeforce.oneOf(
88 | exports.typeforce.Buffer,
89 | exports.typeforce.String,
90 | ),
91 | bip32: {
92 | public: exports.typeforce.UInt32,
93 | private: exports.typeforce.UInt32,
94 | },
95 | pubKeyHash: exports.typeforce.UInt8,
96 | scriptHash: exports.typeforce.UInt8,
97 | wif: exports.typeforce.UInt8,
98 | });
99 | exports.TAPLEAF_VERSION_MASK = 0xfe;
100 | function isTapleaf(o) {
101 | if (!o || !('output' in o)) return false;
102 | if (!buffer_1.Buffer.isBuffer(o.output)) return false;
103 | if (o.version !== undefined)
104 | return (o.version & exports.TAPLEAF_VERSION_MASK) === o.version;
105 | return true;
106 | }
107 | exports.isTapleaf = isTapleaf;
108 | function isTaptree(scriptTree) {
109 | if (!(0, exports.Array)(scriptTree)) return isTapleaf(scriptTree);
110 | if (scriptTree.length !== 2) return false;
111 | return scriptTree.every(t => isTaptree(t));
112 | }
113 | exports.isTaptree = isTaptree;
114 | exports.Buffer256bit = exports.typeforce.BufferN(32);
115 | exports.Hash160bit = exports.typeforce.BufferN(20);
116 | exports.Hash256bit = exports.typeforce.BufferN(32);
117 | exports.Number = exports.typeforce.Number;
118 | exports.Array = exports.typeforce.Array;
119 | exports.Boolean = exports.typeforce.Boolean;
120 | exports.String = exports.typeforce.String;
121 | exports.Buffer = exports.typeforce.Buffer;
122 | exports.Hex = exports.typeforce.Hex;
123 | exports.maybe = exports.typeforce.maybe;
124 | exports.tuple = exports.typeforce.tuple;
125 | exports.UInt8 = exports.typeforce.UInt8;
126 | exports.UInt32 = exports.typeforce.UInt32;
127 | exports.Function = exports.typeforce.Function;
128 | exports.BufferN = exports.typeforce.BufferN;
129 | exports.Null = exports.typeforce.Null;
130 | exports.oneOf = exports.typeforce.oneOf;
131 |
--------------------------------------------------------------------------------
/test/address.spec.ts:
--------------------------------------------------------------------------------
1 | import * as assert from 'assert';
2 | import { describe, it } from 'mocha';
3 | import * as ecc from 'tiny-secp256k1';
4 | import * as baddress from '../src/address';
5 | import * as bscript from '../src/script';
6 | import * as fixtures from './fixtures/address.json';
7 |
8 | import { initEccLib } from '../src';
9 |
10 | const NETWORKS = Object.assign(
11 | {
12 | litecoin: {
13 | messagePrefix: '\x19Litecoin Signed Message:\n',
14 | bip32: {
15 | public: 0x019da462,
16 | private: 0x019d9cfe,
17 | },
18 | pubKeyHash: 0x30,
19 | scriptHash: 0x32,
20 | wif: 0xb0,
21 | },
22 | },
23 | require('../src/networks'),
24 | );
25 |
26 | describe('address', () => {
27 | describe('fromBase58Check', () => {
28 | fixtures.standard.forEach(f => {
29 | if (!f.base58check) return;
30 |
31 | it('decodes ' + f.base58check, () => {
32 | const decode = baddress.fromBase58Check(f.base58check);
33 |
34 | assert.strictEqual(decode.version, f.version);
35 | assert.strictEqual(decode.hash.toString('hex'), f.hash);
36 | });
37 | });
38 |
39 | fixtures.invalid.fromBase58Check.forEach(f => {
40 | it('throws on ' + f.exception, () => {
41 | assert.throws(() => {
42 | baddress.fromBase58Check(f.address);
43 | }, new RegExp(f.address + ' ' + f.exception));
44 | });
45 | });
46 | });
47 |
48 | describe('fromBech32', () => {
49 | fixtures.standard.forEach(f => {
50 | if (!f.bech32) return;
51 |
52 | it('decodes ' + f.bech32, () => {
53 | const actual = baddress.fromBech32(f.bech32);
54 |
55 | assert.strictEqual(actual.version, f.version);
56 | assert.strictEqual(actual.prefix, NETWORKS[f.network].bech32);
57 | assert.strictEqual(actual.data.toString('hex'), f.data);
58 | });
59 | });
60 |
61 | fixtures.invalid.bech32.forEach(f => {
62 | it('decode fails for ' + f.address + '(' + f.exception + ')', () => {
63 | assert.throws(() => {
64 | baddress.fromBech32(f.address);
65 | }, new RegExp(f.exception));
66 | });
67 | });
68 | });
69 |
70 | describe('fromOutputScript', () => {
71 | initEccLib(ecc);
72 | fixtures.standard.forEach(f => {
73 | it('encodes ' + f.script.slice(0, 30) + '... (' + f.network + ')', () => {
74 | const script = bscript.fromASM(f.script);
75 | const address = baddress.fromOutputScript(script, NETWORKS[f.network]);
76 |
77 | assert.strictEqual(address, f.base58check || f.bech32!.toLowerCase());
78 | });
79 | });
80 |
81 | fixtures.invalid.fromOutputScript.forEach(f => {
82 | it('throws when ' + f.script.slice(0, 30) + '... ' + f.exception, () => {
83 | const script = bscript.fromASM(f.script);
84 |
85 | assert.throws(() => {
86 | baddress.fromOutputScript(script, undefined);
87 | }, new RegExp(f.exception));
88 | });
89 | });
90 | });
91 |
92 | describe('toBase58Check', () => {
93 | fixtures.standard.forEach(f => {
94 | if (!f.base58check) return;
95 |
96 | it('encodes ' + f.hash + ' (' + f.network + ')', () => {
97 | const address = baddress.toBase58Check(
98 | Buffer.from(f.hash, 'hex'),
99 | f.version,
100 | );
101 |
102 | assert.strictEqual(address, f.base58check);
103 | });
104 | });
105 | });
106 |
107 | describe('toBech32', () => {
108 | fixtures.bech32.forEach(f => {
109 | if (!f.address) return;
110 | const data = Buffer.from(f.data, 'hex');
111 |
112 | it('encode ' + f.address, () => {
113 | assert.deepStrictEqual(
114 | baddress.toBech32(data, f.version, f.prefix),
115 | f.address.toLowerCase(),
116 | );
117 | });
118 | });
119 |
120 | // TODO: These fixtures (according to TypeScript) have none of the data used below
121 | fixtures.invalid.bech32.forEach((f: any) => {
122 | if (!f.prefix || f.version === undefined || f.data === undefined) return;
123 |
124 | it('encode fails (' + f.exception, () => {
125 | assert.throws(() => {
126 | baddress.toBech32(Buffer.from(f.data, 'hex'), f.version, f.prefix);
127 | }, new RegExp(f.exception));
128 | });
129 | });
130 | });
131 |
132 | describe('toOutputScript', () => {
133 | fixtures.standard.forEach(f => {
134 | it('decodes ' + f.script.slice(0, 30) + '... (' + f.network + ')', () => {
135 | const script = baddress.toOutputScript(
136 | (f.base58check || f.bech32)!,
137 | NETWORKS[f.network],
138 | );
139 |
140 | assert.strictEqual(bscript.toASM(script), f.script);
141 | });
142 | });
143 |
144 | fixtures.invalid.toOutputScript.forEach(f => {
145 | it('throws when ' + (f.exception || f.paymentException), () => {
146 | const exception = f.paymentException || `${f.address} ${f.exception}`;
147 | assert.throws(() => {
148 | baddress.toOutputScript(f.address, f.network as any);
149 | }, new RegExp(exception));
150 | });
151 | });
152 | });
153 | });
154 |
--------------------------------------------------------------------------------
/test/crypto.spec.ts:
--------------------------------------------------------------------------------
1 | import * as assert from 'assert';
2 | import { describe, it } from 'mocha';
3 | import { crypto as bcrypto, TaggedHashPrefix } from '..';
4 | import * as fixtures from './fixtures/crypto.json';
5 | import { sha256, TAGS, TAGGED_HASH_PREFIXES } from '../src/crypto';
6 |
7 | describe('crypto', () => {
8 | ['hash160', 'hash256', 'ripemd160', 'sha1', 'sha256'].forEach(algorithm => {
9 | describe(algorithm, () => {
10 | fixtures.hashes.forEach(f => {
11 | const fn = (bcrypto as any)[algorithm];
12 | const expected = (f as any)[algorithm];
13 |
14 | it('returns ' + expected + ' for ' + f.hex, () => {
15 | const data = Buffer.from(f.hex, 'hex');
16 | const actual = fn(data).toString('hex');
17 |
18 | assert.strictEqual(actual, expected);
19 | });
20 | });
21 | });
22 | });
23 |
24 | describe('taggedHash', () => {
25 | fixtures.taggedHash.forEach(f => {
26 | const bytes = Buffer.from(f.hex, 'hex');
27 | const expected = Buffer.from(f.result, 'hex');
28 | it(`returns ${f.result} for taggedHash "${f.tag}" of ${f.hex}`, () => {
29 | const actual = bcrypto.taggedHash(f.tag as TaggedHashPrefix, bytes);
30 | assert.strictEqual(actual.toString('hex'), expected.toString('hex'));
31 | });
32 | });
33 | });
34 |
35 | describe('TAGGED_HASH_PREFIXES', () => {
36 | const taggedHashPrefixes = Object.fromEntries(
37 | TAGS.map((tag: TaggedHashPrefix) => {
38 | const tagHash = sha256(Buffer.from(tag));
39 | return [tag, Buffer.concat([tagHash, tagHash])];
40 | }),
41 | );
42 | it('stored the result of operation', () => {
43 | assert.strictEqual(
44 | JSON.stringify(TAGGED_HASH_PREFIXES),
45 | JSON.stringify(taggedHashPrefixes),
46 | );
47 | });
48 | });
49 | });
50 |
--------------------------------------------------------------------------------
/test/fixtures/bufferutils.json:
--------------------------------------------------------------------------------
1 | {
2 | "valid": [
3 | {
4 | "dec": 0,
5 | "hex": "0000000000000000"
6 | },
7 | {
8 | "dec": 1,
9 | "hex": "0100000000000000"
10 | },
11 | {
12 | "dec": 252,
13 | "hex": "fc00000000000000"
14 | },
15 | {
16 | "dec": 253,
17 | "hex": "fd00000000000000"
18 | },
19 | {
20 | "dec": 254,
21 | "hex": "fe00000000000000"
22 | },
23 | {
24 | "dec": 255,
25 | "hex": "ff00000000000000"
26 | },
27 | {
28 | "dec": 65534,
29 | "hex": "feff000000000000"
30 | },
31 | {
32 | "dec": 65535,
33 | "hex": "ffff000000000000"
34 | },
35 | {
36 | "dec": 65536,
37 | "hex": "0000010000000000"
38 | },
39 | {
40 | "dec": 65537,
41 | "hex": "0100010000000000"
42 | },
43 | {
44 | "dec": 4294967295,
45 | "hex": "ffffffff00000000"
46 | },
47 | {
48 | "dec": 4294967296,
49 | "hex": "0000000001000000"
50 | },
51 | {
52 | "dec": 4294967297,
53 | "hex": "0100000001000000"
54 | },
55 | {
56 | "dec": 9007199254740991,
57 | "hex": "ffffffffffff1f00"
58 | }
59 | ],
60 | "invalid": {
61 | "readUInt64LE": [
62 | {
63 | "description": "n === 2^53",
64 | "exception": "RangeError: value out of range",
65 | "hex": "0000000000002000",
66 | "dec": 9007199254740992
67 | },
68 | {
69 | "description": "n > 2^53",
70 | "exception": "RangeError: value out of range",
71 | "hex": "0100000000002000",
72 | "dec": 9007199254740993
73 | }
74 | ],
75 | "writeUInt64LE": [
76 | {
77 | "description": "n === 2^53",
78 | "exception": "RangeError: value out of range",
79 | "hex": "0000000000002000",
80 | "dec": 9007199254740992
81 | },
82 | {
83 | "description": "n > 2^53",
84 | "exception": "RangeError: value out of range",
85 | "hex": "0100000000002000",
86 | "dec": 9007199254740993
87 | },
88 | {
89 | "description": "n < 0",
90 | "exception": "specified a negative value for writing an unsigned value",
91 | "hex": "",
92 | "dec": -1
93 | },
94 | {
95 | "description": "0 < n < 1",
96 | "exception": "value has a fractional component",
97 | "hex": "",
98 | "dec": 0.1
99 | }
100 | ]
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/test/fixtures/core/README.md:
--------------------------------------------------------------------------------
1 | Description
2 | ------------
3 |
4 | This directory contains data-driven tests for various aspects of Bitcoin.
5 |
6 |
7 | Bitcoinjs-lib notes
8 | -------------------
9 |
10 | This directory does not contain all the Bitcoin core tests.
11 | Missing core test data includes:
12 |
13 | * `alertTests.raw`
14 | Bitcoin-js does not interact with the Bitcoin network directly.
15 |
16 | * `tx_invalid.json`
17 | Bitcoin-js can not evaluate Scripts, making testing this irrelevant.
18 | It can decode valid Transactions, therefore `tx_valid.json` remains.
19 |
20 | * `script*.json`
21 | Bitcoin-js can not evaluate Scripts, making testing this irrelevant.
22 |
23 |
24 | License
25 | --------
26 |
27 | The data files in this directory are
28 |
29 | Copyright (c) 2012-2014 The Bitcoin Core developers
30 | Distributed under the MIT/X11 software license, see the accompanying
31 | file COPYING or http://www.opensource.org/licenses/mit-license.php.
32 |
--------------------------------------------------------------------------------
/test/fixtures/core/base58_encode_decode.json:
--------------------------------------------------------------------------------
1 | [
2 | ["", ""],
3 | ["61", "2g"],
4 | ["626262", "a3gV"],
5 | ["636363", "aPEr"],
6 | ["73696d706c792061206c6f6e6720737472696e67", "2cFupjhnEsSn59qHXstmK2ffpLv2"],
7 | ["00eb15231dfceb60925886b67d065299925915aeb172c06647", "1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L"],
8 | ["516b6fcd0f", "ABnLTmg"],
9 | ["bf4f89001e670274dd", "3SEo3LWLoPntC"],
10 | ["572e4794", "3EFU7m"],
11 | ["ecac89cad93923c02321", "EJDM8drfXA6uyA"],
12 | ["10c8511e", "Rt5zm"],
13 | ["00000000000000000000", "1111111111"]
14 | ]
15 |
--------------------------------------------------------------------------------
/test/fixtures/core/base58_keys_invalid.json:
--------------------------------------------------------------------------------
1 | [
2 | [
3 | ""
4 | ],
5 | [
6 | "x"
7 | ],
8 | [
9 | "37qgekLpCCHrQuSjvX3fs496FWTGsHFHizjJAs6NPcR47aefnnCWECAhHV6E3g4YN7u7Yuwod5Y"
10 | ],
11 | [
12 | "dzb7VV1Ui55BARxv7ATxAtCUeJsANKovDGWFVgpTbhq9gvPqP3yv"
13 | ],
14 | [
15 | "MuNu7ZAEDFiHthiunm7dPjwKqrVNCM3mAz6rP9zFveQu14YA8CxExSJTHcVP9DErn6u84E6Ej7S"
16 | ],
17 | [
18 | "rPpQpYknyNQ5AEHuY6H8ijJJrYc2nDKKk9jjmKEXsWzyAQcFGpDLU2Zvsmoi8JLR7hAwoy3RQWf"
19 | ],
20 | [
21 | "4Uc3FmN6NQ6zLBK5QQBXRBUREaaHwCZYsGCueHauuDmJpZKn6jkEskMB2Zi2CNgtb5r6epWEFfUJq"
22 | ],
23 | [
24 | "7aQgR5DFQ25vyXmqZAWmnVCjL3PkBcdVkBUpjrjMTcghHx3E8wb"
25 | ],
26 | [
27 | "17QpPprjeg69fW1DV8DcYYCKvWjYhXvWkov6MJ1iTTvMFj6weAqW7wybZeH57WTNxXVCRH4veVs"
28 | ],
29 | [
30 | "KxuACDviz8Xvpn1xAh9MfopySZNuyajYMZWz16Dv2mHHryznWUp3"
31 | ],
32 | [
33 | "7nK3GSmqdXJQtdohvGfJ7KsSmn3TmGqExug49583bDAL91pVSGq5xS9SHoAYL3Wv3ijKTit65th"
34 | ],
35 | [
36 | "cTivdBmq7bay3RFGEBBuNfMh2P1pDCgRYN2Wbxmgwr4ki3jNUL2va"
37 | ],
38 | [
39 | "gjMV4vjNjyMrna4fsAr8bWxAbwtmMUBXJS3zL4NJt5qjozpbQLmAfK1uA3CquSqsZQMpoD1g2nk"
40 | ],
41 | [
42 | "emXm1naBMoVzPjbk7xpeTVMFy4oDEe25UmoyGgKEB1gGWsK8kRGs"
43 | ],
44 | [
45 | "7VThQnNRj1o3Zyvc7XHPRrjDf8j2oivPTeDXnRPYWeYGE4pXeRJDZgf28ppti5hsHWXS2GSobdqyo"
46 | ],
47 | [
48 | "1G9u6oCVCPh2o8m3t55ACiYvG1y5BHewUkDSdiQarDcYXXhFHYdzMdYfUAhfxn5vNZBwpgUNpso"
49 | ],
50 | [
51 | "31QQ7ZMLkScDiB4VyZjuptr7AEc9j1SjstF7pRoLhHTGkW4Q2y9XELobQmhhWxeRvqcukGd1XCq"
52 | ],
53 | [
54 | "DHqKSnpxa8ZdQyH8keAhvLTrfkyBMQxqngcQA5N8LQ9KVt25kmGN"
55 | ],
56 | [
57 | "2LUHcJPbwLCy9GLH1qXmfmAwvadWw4bp4PCpDfduLqV17s6iDcy1imUwhQJhAoNoN1XNmweiJP4i"
58 | ],
59 | [
60 | "7USRzBXAnmck8fX9HmW7RAb4qt92VFX6soCnts9s74wxm4gguVhtG5of8fZGbNPJA83irHVY6bCos"
61 | ],
62 | [
63 | "1DGezo7BfVebZxAbNT3XGujdeHyNNBF3vnficYoTSp4PfK2QaML9bHzAMxke3wdKdHYWmsMTJVu"
64 | ],
65 | [
66 | "2D12DqDZKwCxxkzs1ZATJWvgJGhQ4cFi3WrizQ5zLAyhN5HxuAJ1yMYaJp8GuYsTLLxTAz6otCfb"
67 | ],
68 | [
69 | "8AFJzuTujXjw1Z6M3fWhQ1ujDW7zsV4ePeVjVo7D1egERqSW9nZ"
70 | ],
71 | [
72 | "163Q17qLbTCue8YY3AvjpUhotuaodLm2uqMhpYirsKjVqnxJRWTEoywMVY3NbBAHuhAJ2cF9GAZ"
73 | ],
74 | [
75 | "2MnmgiRH4eGLyLc9eAqStzk7dFgBjFtUCtu"
76 | ],
77 | [
78 | "461QQ2sYWxU7H2PV4oBwJGNch8XVTYYbZxU"
79 | ],
80 | [
81 | "2UCtv53VttmQYkVU4VMtXB31REvQg4ABzs41AEKZ8UcB7DAfVzdkV9JDErwGwyj5AUHLkmgZeobs"
82 | ],
83 | [
84 | "cSNjAsnhgtiFMi6MtfvgscMB2Cbhn2v1FUYfviJ1CdjfidvmeW6mn"
85 | ],
86 | [
87 | "gmsow2Y6EWAFDFE1CE4Hd3Tpu2BvfmBfG1SXsuRARbnt1WjkZnFh1qGTiptWWbjsq2Q6qvpgJVj"
88 | ],
89 | [
90 | "nksUKSkzS76v8EsSgozXGMoQFiCoCHzCVajFKAXqzK5on9ZJYVHMD5CKwgmX3S3c7M1U3xabUny"
91 | ],
92 | [
93 | "L3favK1UzFGgdzYBF2oBT5tbayCo4vtVBLJhg2iYuMeePxWG8SQc"
94 | ],
95 | [
96 | "7VxLxGGtYT6N99GdEfi6xz56xdQ8nP2dG1CavuXx7Rf2PrvNMTBNevjkfgs9JmkcGm6EXpj8ipyPZ"
97 | ],
98 | [
99 | "2mbZwFXF6cxShaCo2czTRB62WTx9LxhTtpP"
100 | ],
101 | [
102 | "dB7cwYdcPSgiyAwKWL3JwCVwSk6epU2txw"
103 | ],
104 | [
105 | "HPhFUhUAh8ZQQisH8QQWafAxtQYju3SFTX"
106 | ],
107 | [
108 | "4ctAH6AkHzq5ioiM1m9T3E2hiYEev5mTsB"
109 | ],
110 | [
111 | "Hn1uFi4dNexWrqARpjMqgT6cX1UsNPuV3cHdGg9ExyXw8HTKadbktRDtdeVmY3M1BxJStiL4vjJ"
112 | ],
113 | [
114 | "Sq3fDbvutABmnAHHExJDgPLQn44KnNC7UsXuT7KZecpaYDMU9Txs"
115 | ],
116 | [
117 | "6TqWyrqdgUEYDQU1aChMuFMMEimHX44qHFzCUgGfqxGgZNMUVWJ"
118 | ],
119 | [
120 | "giqJo7oWqFxNKWyrgcBxAVHXnjJ1t6cGoEffce5Y1y7u649Noj5wJ4mmiUAKEVVrYAGg2KPB3Y4"
121 | ],
122 | [
123 | "cNzHY5e8vcmM3QVJUcjCyiKMYfeYvyueq5qCMV3kqcySoLyGLYUK"
124 | ],
125 | [
126 | "37uTe568EYc9WLoHEd9jXEvUiWbq5LFLscNyqvAzLU5vBArUJA6eydkLmnMwJDjkL5kXc2VK7ig"
127 | ],
128 | [
129 | "EsYbG4tWWWY45G31nox838qNdzksbPySWc"
130 | ],
131 | [
132 | "nbuzhfwMoNzA3PaFnyLcRxE9bTJPDkjZ6Rf6Y6o2ckXZfzZzXBT"
133 | ],
134 | [
135 | "cQN9PoxZeCWK1x56xnz6QYAsvR11XAce3Ehp3gMUdfSQ53Y2mPzx"
136 | ],
137 | [
138 | "1Gm3N3rkef6iMbx4voBzaxtXcmmiMTqZPhcuAepRzYUJQW4qRpEnHvMojzof42hjFRf8PE2jPde"
139 | ],
140 | [
141 | "2TAq2tuN6x6m233bpT7yqdYQPELdTDJn1eU"
142 | ],
143 | [
144 | "ntEtnnGhqPii4joABvBtSEJG6BxjT2tUZqE8PcVYgk3RHpgxgHDCQxNbLJf7ardf1dDk2oCQ7Cf"
145 | ],
146 | [
147 | "Ky1YjoZNgQ196HJV3HpdkecfhRBmRZdMJk89Hi5KGfpfPwS2bUbfd"
148 | ],
149 | [
150 | "2A1q1YsMZowabbvta7kTy2Fd6qN4r5ZCeG3qLpvZBMzCixMUdkN2Y4dHB1wPsZAeVXUGD83MfRED"
151 | ]
152 | ]
153 |
--------------------------------------------------------------------------------
/test/fixtures/core/sig_canonical.json:
--------------------------------------------------------------------------------
1 | [
2 | "300602010002010001",
3 | "3008020200ff020200ff01",
4 | "304402203932c892e2e550f3af8ee4ce9c215a87f9bb831dcac87b2838e2c2eaa891df0c022030b61dd36543125d56b9f9f3a1f9353189e5af33cdda8d77a5209aec03978fa001",
5 | "30450220076045be6f9eca28ff1ec606b833d0b87e70b2a630f5e3a496b110967a40f90a0221008fffd599910eefe00bc803c688c2eca1d2ba7f6b180620eaa03488e6585db6ba01",
6 | "3046022100876045be6f9eca28ff1ec606b833d0b87e70b2a630f5e3a496b110967a40f90a0221008fffd599910eefe00bc803c688c2eca1d2ba7f6b180620eaa03488e6585db6ba01"
7 | ]
8 |
--------------------------------------------------------------------------------
/test/fixtures/core/sig_noncanonical.json:
--------------------------------------------------------------------------------
1 | [
2 | "non-hex strings are ignored",
3 |
4 | "too short:", "30050201FF020001",
5 | "too long:", "30470221005990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba6105022200002d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed01",
6 | "hashtype:", "304402205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba610502202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed11",
7 | "type:", "314402205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba610502202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed01",
8 | "total length:", "304502205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba610502202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed01",
9 | "S len oob:", "301F01205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb101",
10 | "R+S:", "304502205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba610502202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed0001",
11 |
12 | "R type:", "304401205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba610502202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed01",
13 | "R len = 0:", "3024020002202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed01",
14 | "R<0:", "304402208990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba610502202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed01",
15 | "R padded:", "30450221005990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba610502202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed01",
16 |
17 |
18 | "S type:", "304402205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba610501202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed01",
19 | "S len = 0:", "302402205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba6105020001",
20 | "S<0:", "304402205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba61050220fd5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed01",
21 | "S padded:", "304502205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba61050221002d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed01"
22 | ]
23 |
--------------------------------------------------------------------------------
/test/fixtures/crypto.json:
--------------------------------------------------------------------------------
1 | {
2 | "hashes": [
3 | {
4 | "hex": "0000000000000001",
5 | "hash160": "cdb00698f02afd929ffabea308340fa99ac2afa8",
6 | "hash256": "3ae5c198d17634e79059c2cd735491553d22c4e09d1d9fea3ecf214565df2284",
7 | "ripemd160": "8d1a05d1bc08870968eb8a81ad4393fd3aac6633",
8 | "sha1": "cb473678976f425d6ec1339838f11011007ad27d",
9 | "sha256": "cd2662154e6d76b2b2b92e70c0cac3ccf534f9b74eb5b89819ec509083d00a50"
10 | },
11 | {
12 | "hex": "0101010101010101",
13 | "hash160": "abaf1119f83e384210fe8e222eac76e2f0da39dc",
14 | "hash256": "728338d99f356175c4945ef5cccfa61b7b56143cbbf426ddd0e0fc7cfe8c3c23",
15 | "ripemd160": "5825701b4b9767fd35063b286dca3582853e0630",
16 | "sha1": "c0357a32ed1f6a03be92dd094476f7f1a2e214ec",
17 | "sha256": "04abc8821a06e5a30937967d11ad10221cb5ac3b5273e434f1284ee87129a061"
18 | },
19 | {
20 | "hex": "ffffffffffffffff",
21 | "hash160": "f86221f5a1fca059a865c0b7d374dfa9d5f3aeb4",
22 | "hash256": "752adad0a7b9ceca853768aebb6965eca126a62965f698a0c1bc43d83db632ad",
23 | "ripemd160": "cb760221600ed34337ca3ab70016b5f58c838120",
24 | "sha1": "be673e8a56eaa9d8c1d35064866701c11ef8e089",
25 | "sha256": "12a3ae445661ce5dee78d0650d33362dec29c4f82af05e7e57fb595bbbacf0ca"
26 | },
27 | {
28 | "hex": "4c6f72656d20697073756d20646f6c6f722073697420616d65742c20636f6e73656374657475722061646970697363696e6720656c69742e20446f6e65632061742066617563696275732073617069656e2c2076656c20666163696c6973697320617263752e20536564207574206d61737361206e6962682e205574206d6f6c6c69732070756c76696e6172206d617373612e20557420756c6c616d636f7270657220646f6c6f7220656e696d2c20696e206d6f6c657374696520656e696d20636f6e64696d656e74756d2061632e20416c697175616d206572617420766f6c75747061742e204e756c6c6120736f64616c657320617420647569206e656320",
29 | "hash160": "9763e6b367c363bd6b88a7b361c98e6beee243a5",
30 | "hash256": "033588797115feb3545052670cac2a46584ab3cb460de63756ee0275e66b5799",
31 | "ripemd160": "cad8593dcdef12ee334c97bab9787f07b3f3a1a5",
32 | "sha1": "10d96fb43aca84e342206887bbeed3065d4e4344",
33 | "sha256": "a7fb8276035057ed6479c5f2305a96da100ac43f0ac10f277e5ab8c5457429da"
34 | }
35 | ],
36 | "taggedHash": [
37 | {
38 | "tag": "TapTweak",
39 | "hex": "0101010101010101",
40 | "result": "71ae15bad52efcecf4c9f672bfbded68a4adb8258f1b95f0d06aefdb5ebd14e9"
41 | }
42 | ]
43 | }
--------------------------------------------------------------------------------
/test/fixtures/embed.json:
--------------------------------------------------------------------------------
1 | {
2 | "valid": [
3 | {
4 | "description": "output from output",
5 | "arguments": {
6 | "output": "OP_RETURN a3b147dbe4a85579fc4b5a1811e76620560e07267e62b9a0d6858f9127735cadd82f67e06c24dbc4"
7 | },
8 | "options": {},
9 | "expected": {
10 | "data": [
11 | "a3b147dbe4a85579fc4b5a1811e76620560e07267e62b9a0d6858f9127735cadd82f67e06c24dbc4"
12 | ],
13 | "output": "OP_RETURN a3b147dbe4a85579fc4b5a1811e76620560e07267e62b9a0d6858f9127735cadd82f67e06c24dbc4",
14 | "input": null,
15 | "witness": null
16 | }
17 | },
18 | {
19 | "description": "output from data",
20 | "arguments": {
21 | "data": [
22 | "a3b147dbe4a85579fc4b5a1811e76620560e07267e62b9a0d6858f9127735cadd82f67e06c24dbc4"
23 | ]
24 | },
25 | "expected": {
26 | "data": [
27 | "a3b147dbe4a85579fc4b5a1811e76620560e07267e62b9a0d6858f9127735cadd82f67e06c24dbc4"
28 | ],
29 | "output": "OP_RETURN a3b147dbe4a85579fc4b5a1811e76620560e07267e62b9a0d6858f9127735cadd82f67e06c24dbc4",
30 | "input": null,
31 | "witness": null
32 | }
33 | }
34 | ],
35 | "invalid": [
36 | {
37 | "exception": "Not enough data",
38 | "arguments": {}
39 | },
40 | {
41 | "description": "First OP is not OP_RETURN",
42 | "exception": "Output is invalid",
43 | "options": {},
44 | "arguments": {
45 | "output": "OP_1 OP_2 OP_ADD"
46 | }
47 | },
48 | {
49 | "description": "Return value and data do not match",
50 | "exception": "Data mismatch",
51 | "options": {},
52 | "arguments": {
53 | "output": "OP_RETURN a3b147dbe4a85579fc4b5a1811e76620560e07267e62b9a0d6858f9127735cadd82f67e06c24dbc4",
54 | "data": [
55 | "a3b147dbe4a85579fc4b5a1855555555555555555555555555555555555555555555555555555555"
56 | ]
57 | }
58 | },
59 | {
60 | "description": "Script length incorrect",
61 | "exception": "Data mismatch",
62 | "options": {},
63 | "arguments": {
64 | "output": "OP_RETURN a3b1 47db",
65 | "data": [
66 | "a3b1"
67 | ]
68 | }
69 | },
70 | {
71 | "description": "Return data is not buffer",
72 | "exception": "Output is invalid",
73 | "options": {},
74 | "arguments": {
75 | "output": "OP_RETURN OP_1"
76 | }
77 | }
78 | ],
79 | "dynamic": {
80 | "depends": {
81 | "data": [ "data", "output" ],
82 | "output": [ "output", "data" ]
83 | },
84 | "details": [
85 | {
86 | "description": "embed",
87 | "data": [
88 | "a3b147dbe4a85579fc4b5a1811e76620560e07267e62b9a0d6858f9127735cadd82f67e06c24dbc4"
89 | ],
90 | "output": "OP_RETURN a3b147dbe4a85579fc4b5a1811e76620560e07267e62b9a0d6858f9127735cadd82f67e06c24dbc4"
91 | },
92 | {
93 | "description": "embed",
94 | "data": [
95 | "a3b147dbe4a85579fc4b5a1811e76620560e0726",
96 | "7e62b9a0d6858f9127735cadd82f67e06c24dbc4"
97 | ],
98 | "output": "OP_RETURN a3b147dbe4a85579fc4b5a1811e76620560e0726 7e62b9a0d6858f9127735cadd82f67e06c24dbc4"
99 | }
100 | ]
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/test/fixtures/script_number.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "hex": "",
4 | "number": 0
5 | },
6 | {
7 | "hex": "01",
8 | "number": 1
9 | },
10 | {
11 | "hex": "02",
12 | "number": 2
13 | },
14 | {
15 | "hex": "03",
16 | "number": 3
17 | },
18 | {
19 | "hex": "7e",
20 | "number": 126
21 | },
22 | {
23 | "hex": "7f",
24 | "number": 127
25 | },
26 | {
27 | "hex": "8000",
28 | "number": 128
29 | },
30 | {
31 | "hex": "8100",
32 | "number": 129
33 | },
34 | {
35 | "hex": "8200",
36 | "number": 130
37 | },
38 | {
39 | "hex": "ff00",
40 | "number": 255
41 | },
42 | {
43 | "hex": "fe7f",
44 | "number": 32766
45 | },
46 | {
47 | "hex": "ff7f",
48 | "number": 32767
49 | },
50 | {
51 | "hex": "008000",
52 | "number": 32768
53 | },
54 | {
55 | "hex": "ffff00",
56 | "number": 65535
57 | },
58 | {
59 | "hex": "018000",
60 | "number": 32769
61 | },
62 | {
63 | "hex": "028000",
64 | "number": 32770
65 | },
66 | {
67 | "hex": "ffffff00",
68 | "number": 16777215
69 | },
70 | {
71 | "hex": "feff7f",
72 | "number": 8388606
73 | },
74 | {
75 | "hex": "ffff7f",
76 | "number": 8388607
77 | },
78 | {
79 | "hex": "00008000",
80 | "number": 8388608
81 | },
82 | {
83 | "hex": "01008000",
84 | "number": 8388609
85 | },
86 | {
87 | "hex": "02008000",
88 | "number": 8388610
89 | },
90 | {
91 | "hex": "feffff7f",
92 | "number": 2147483646
93 | },
94 | {
95 | "hex": "ffffff7f",
96 | "number": 2147483647
97 | },
98 | {
99 | "bytes": 5,
100 | "hex": "0000008000",
101 | "number": 2147483648
102 | },
103 | {
104 | "bytes": 5,
105 | "hex": "0100008000",
106 | "number": 2147483649
107 | },
108 | {
109 | "bytes": 5,
110 | "hex": "0200008000",
111 | "number": 2147483650
112 | },
113 | {
114 | "bytes": 5,
115 | "hex": "ffffffff00",
116 | "number": 4294967295
117 | },
118 | {
119 | "bytes": 5,
120 | "hex": "0200008080",
121 | "number": -2147483650
122 | },
123 | {
124 | "bytes": 5,
125 | "hex": "0100008080",
126 | "number": -2147483649
127 | },
128 | {
129 | "bytes": 5,
130 | "hex": "0000008080",
131 | "number": -2147483648
132 | },
133 | {
134 | "hex": "ffffffff",
135 | "number": -2147483647
136 | },
137 | {
138 | "hex": "feffffff",
139 | "number": -2147483646
140 | },
141 | {
142 | "hex": "fdffffff",
143 | "number": -2147483645
144 | },
145 | {
146 | "hex": "ffffff80",
147 | "number": -16777215
148 | },
149 | {
150 | "hex": "01008080",
151 | "number": -8388609
152 | },
153 | {
154 | "hex": "00008080",
155 | "number": -8388608
156 | },
157 | {
158 | "hex": "ffffff",
159 | "number": -8388607
160 | },
161 | {
162 | "hex": "feffff",
163 | "number": -8388606
164 | },
165 | {
166 | "hex": "fdffff",
167 | "number": -8388605
168 | },
169 | {
170 | "hex": "ffff80",
171 | "number": -65535
172 | },
173 | {
174 | "hex": "018080",
175 | "number": -32769
176 | },
177 | {
178 | "hex": "008080",
179 | "number": -32768
180 | },
181 | {
182 | "hex": "ffff",
183 | "number": -32767
184 | },
185 | {
186 | "hex": "feff",
187 | "number": -32766
188 | },
189 | {
190 | "hex": "fdff",
191 | "number": -32765
192 | },
193 | {
194 | "hex": "ff80",
195 | "number": -255
196 | },
197 | {
198 | "hex": "8180",
199 | "number": -129
200 | },
201 | {
202 | "hex": "8080",
203 | "number": -128
204 | },
205 | {
206 | "hex": "ff",
207 | "number": -127
208 | },
209 | {
210 | "hex": "fe",
211 | "number": -126
212 | },
213 | {
214 | "hex": "fd",
215 | "number": -125
216 | },
217 | {
218 | "hex": "82",
219 | "number": -2
220 | },
221 | {
222 | "hex": "81",
223 | "number": -1
224 | }
225 | ]
226 |
--------------------------------------------------------------------------------
/test/integration/_regtest.ts:
--------------------------------------------------------------------------------
1 | import { RegtestUtils } from 'regtest-client';
2 |
3 | const APIPASS = process.env.APIPASS || 'satoshi';
4 | const APIURL = process.env.APIURL || 'https://regtest.bitbank.cc/1';
5 |
6 | export const regtestUtils = new RegtestUtils({ APIPASS, APIURL });
7 |
--------------------------------------------------------------------------------
/test/integration/bip32.spec.ts:
--------------------------------------------------------------------------------
1 | import * as assert from 'assert';
2 | import BIP32Factory from 'bip32';
3 | import * as ecc from 'tiny-secp256k1';
4 | import * as bip39 from 'bip39';
5 | import { describe, it } from 'mocha';
6 | import * as bitcoin from '../..';
7 |
8 | const bip32 = BIP32Factory(ecc);
9 |
10 | function getAddress(node: any, network?: any): string {
11 | return bitcoin.payments.p2pkh({ pubkey: node.publicKey, network }).address!;
12 | }
13 |
14 | describe('bitcoinjs-lib (BIP32)', () => {
15 | it('can import a BIP32 testnet xpriv and export to WIF', () => {
16 | const xpriv =
17 | 'tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK';
18 | const node = bip32.fromBase58(xpriv, bitcoin.networks.testnet);
19 |
20 | assert.strictEqual(
21 | node.toWIF(),
22 | 'cQfoY67cetFNunmBUX5wJiw3VNoYx3gG9U9CAofKE6BfiV1fSRw7',
23 | );
24 | });
25 |
26 | it('can export a BIP32 xpriv, then import it', () => {
27 | const mnemonic =
28 | 'praise you muffin lion enable neck grocery crumble super myself license ghost';
29 | const seed = bip39.mnemonicToSeedSync(mnemonic);
30 | const node = bip32.fromSeed(seed);
31 | const strng = node.toBase58();
32 | const restored = bip32.fromBase58(strng);
33 |
34 | assert.strictEqual(getAddress(node), getAddress(restored)); // same public key
35 | assert.strictEqual(node.toWIF(), restored.toWIF()); // same private key
36 | });
37 |
38 | it('can export a BIP32 xpub', () => {
39 | const mnemonic =
40 | 'praise you muffin lion enable neck grocery crumble super myself license ghost';
41 | const seed = bip39.mnemonicToSeedSync(mnemonic);
42 | const node = bip32.fromSeed(seed);
43 | const strng = node.neutered().toBase58();
44 |
45 | assert.strictEqual(
46 | strng,
47 | 'xpub661MyMwAqRbcGhVeaVfEBA25e3cP9DsJQZoE8iep5fZSxy3TnPBNBgWnMZx56oreNc48ZoTkQfatNJ9VWnQ7ZcLZcVStpaXLTeG8bGrzX3n',
48 | );
49 | });
50 |
51 | it('can create a BIP32, bitcoin, account 0, external address', () => {
52 | const path = "m/0'/0/0";
53 | const root = bip32.fromSeed(
54 | Buffer.from(
55 | 'dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd',
56 | 'hex',
57 | ),
58 | );
59 |
60 | const child1 = root.derivePath(path);
61 |
62 | // option 2, manually
63 | const child1b = root.deriveHardened(0).derive(0).derive(0);
64 |
65 | assert.strictEqual(
66 | getAddress(child1),
67 | '1JHyB1oPXufr4FXkfitsjgNB5yRY9jAaa7',
68 | );
69 | assert.strictEqual(
70 | getAddress(child1b),
71 | '1JHyB1oPXufr4FXkfitsjgNB5yRY9jAaa7',
72 | );
73 | });
74 |
75 | it('can create a BIP44, bitcoin, account 0, external address', () => {
76 | const root = bip32.fromSeed(
77 | Buffer.from(
78 | 'dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd',
79 | 'hex',
80 | ),
81 | );
82 |
83 | const child1 = root.derivePath("m/44'/0'/0'/0/0");
84 |
85 | // option 2, manually
86 | const child1b = root
87 | .deriveHardened(44)
88 | .deriveHardened(0)
89 | .deriveHardened(0)
90 | .derive(0)
91 | .derive(0);
92 |
93 | assert.strictEqual(
94 | getAddress(child1),
95 | '12Tyvr1U8A3ped6zwMEU5M8cx3G38sP5Au',
96 | );
97 | assert.strictEqual(
98 | getAddress(child1b),
99 | '12Tyvr1U8A3ped6zwMEU5M8cx3G38sP5Au',
100 | );
101 | });
102 |
103 | it('can create a BIP49, bitcoin testnet, account 0, external address', () => {
104 | const mnemonic =
105 | 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about';
106 | const seed = bip39.mnemonicToSeedSync(mnemonic);
107 | const root = bip32.fromSeed(seed);
108 |
109 | const path = "m/49'/1'/0'/0/0";
110 | const child = root.derivePath(path);
111 |
112 | const { address } = bitcoin.payments.p2sh({
113 | redeem: bitcoin.payments.p2wpkh({
114 | pubkey: child.publicKey,
115 | network: bitcoin.networks.testnet,
116 | }),
117 | network: bitcoin.networks.testnet,
118 | });
119 | assert.strictEqual(address, '2Mww8dCYPUpKHofjgcXcBCEGmniw9CoaiD2');
120 | });
121 |
122 | it('can use BIP39 to generate BIP32 addresses', () => {
123 | // var mnemonic = bip39.generateMnemonic()
124 | const mnemonic =
125 | 'praise you muffin lion enable neck grocery crumble super myself license ghost';
126 | assert(bip39.validateMnemonic(mnemonic));
127 |
128 | const seed = bip39.mnemonicToSeedSync(mnemonic);
129 | const root = bip32.fromSeed(seed);
130 |
131 | // receive addresses
132 | assert.strictEqual(
133 | getAddress(root.derivePath("m/0'/0/0")),
134 | '1AVQHbGuES57wD68AJi7Gcobc3RZrfYWTC',
135 | );
136 | assert.strictEqual(
137 | getAddress(root.derivePath("m/0'/0/1")),
138 | '1Ad6nsmqDzbQo5a822C9bkvAfrYv9mc1JL',
139 | );
140 |
141 | // change addresses
142 | assert.strictEqual(
143 | getAddress(root.derivePath("m/0'/1/0")),
144 | '1349KVc5NgedaK7DvuD4xDFxL86QN1Hvdn',
145 | );
146 | assert.strictEqual(
147 | getAddress(root.derivePath("m/0'/1/1")),
148 | '1EAvj4edpsWcSer3duybAd4KiR4bCJW5J6',
149 | );
150 | });
151 | });
152 |
--------------------------------------------------------------------------------
/test/integration/blocks.spec.ts:
--------------------------------------------------------------------------------
1 | import * as assert from 'assert';
2 | import { describe, it } from 'mocha';
3 | import * as bitcoin from '../..';
4 |
5 | describe('bitcoinjs-lib (blocks)', () => {
6 | it('can extract a height from a CoinBase transaction', () => {
7 | // from 00000000000000000097669cdca131f24d40c4cc7d80eaa65967a2d09acf6ce6
8 | const txHex =
9 | '010000000001010000000000000000000000000000000000000000000000000000000' +
10 | '000000000ffffffff50037f9a07174d696e656420627920416e74506f6f6c685b205a' +
11 | '2b1f7bfabe6d6d36afe1910eca9405b66f97750940a656e38e2c0312958190ff8e98f' +
12 | 'd16761d220400000000000000aa340000d49f0000ffffffff02b07fc3660000000019' +
13 | '76a9148349212dc27ce3ab4c5b29b85c4dec643d764b1788ac0000000000000000266' +
14 | 'a24aa21a9ed72d9432948505e3d3062f1307a3f027a5dea846ff85e47159680919c12' +
15 | 'bf1e40012000000000000000000000000000000000000000000000000000000000000' +
16 | '0000000000000';
17 | const tx = bitcoin.Transaction.fromHex(txHex);
18 |
19 | assert.strictEqual(tx.ins.length, 1);
20 | const script = tx.ins[0].script;
21 | // bitcoin.script.decompile(script) // returns [] :(
22 |
23 | assert.strictEqual(script[0], 0x03);
24 | const heightBuffer = script.slice(1, 4);
25 | const height = bitcoin.script.number.decode(heightBuffer);
26 | assert.strictEqual(height, 498303);
27 | });
28 | });
29 |
--------------------------------------------------------------------------------
/test/integration/payments.spec.ts:
--------------------------------------------------------------------------------
1 | import ECPairFactory from 'ecpair';
2 | import * as ecc from 'tiny-secp256k1';
3 | import { describe, it } from 'mocha';
4 | import * as bitcoin from '../..';
5 | import { regtestUtils } from './_regtest';
6 |
7 | const ECPair = ECPairFactory(ecc);
8 | const NETWORK = regtestUtils.network;
9 | const keyPairs = [
10 | ECPair.makeRandom({ network: NETWORK }),
11 | ECPair.makeRandom({ network: NETWORK }),
12 | ];
13 |
14 | async function buildAndSign(
15 | depends: any,
16 | prevOutput: any,
17 | redeemScript: any,
18 | witnessScript: any,
19 | ): Promise {
20 | const unspent = await regtestUtils.faucetComplex(prevOutput, 5e4);
21 | const utx = await regtestUtils.fetch(unspent.txId);
22 |
23 | const psbt = new bitcoin.Psbt({ network: NETWORK })
24 | .addInput({
25 | hash: unspent.txId,
26 | index: unspent.vout,
27 | nonWitnessUtxo: Buffer.from(utx.txHex, 'hex'),
28 | ...(redeemScript ? { redeemScript } : {}),
29 | ...(witnessScript ? { witnessScript } : {}),
30 | })
31 | .addOutput({
32 | address: regtestUtils.RANDOM_ADDRESS,
33 | value: 2e4,
34 | });
35 |
36 | if (depends.signatures) {
37 | keyPairs.forEach(keyPair => {
38 | psbt.signInput(0, keyPair);
39 | });
40 | } else if (depends.signature) {
41 | psbt.signInput(0, keyPairs[0]);
42 | }
43 |
44 | return regtestUtils.broadcast(
45 | psbt.finalizeAllInputs().extractTransaction().toHex(),
46 | );
47 | }
48 |
49 | ['p2ms', 'p2pk', 'p2pkh', 'p2wpkh'].forEach(k => {
50 | const fixtures = require('../fixtures/' + k);
51 | const { depends } = fixtures.dynamic;
52 | const fn: any = (bitcoin.payments as any)[k];
53 |
54 | const base: any = {};
55 | if (depends.pubkey) base.pubkey = keyPairs[0].publicKey;
56 | if (depends.pubkeys) base.pubkeys = keyPairs.map(x => x.publicKey);
57 | if (depends.m) base.m = base.pubkeys.length;
58 |
59 | const { output } = fn(base);
60 | if (!output) throw new TypeError('Missing output');
61 |
62 | describe('bitcoinjs-lib (payments - ' + k + ')', () => {
63 | it('can broadcast as an output, and be spent as an input', async () => {
64 | Object.assign(depends, { prevOutScriptType: k });
65 | await buildAndSign(depends, output, undefined, undefined);
66 | });
67 |
68 | it(
69 | 'can (as P2SH(' +
70 | k +
71 | ')) broadcast as an output, and be spent as an input',
72 | async () => {
73 | const p2sh = bitcoin.payments.p2sh({
74 | redeem: { output },
75 | network: NETWORK,
76 | });
77 | Object.assign(depends, { prevOutScriptType: 'p2sh-' + k });
78 | await buildAndSign(
79 | depends,
80 | p2sh.output,
81 | p2sh.redeem!.output,
82 | undefined,
83 | );
84 | },
85 | );
86 |
87 | // NOTE: P2WPKH cannot be wrapped in P2WSH, consensus fail
88 | if (k === 'p2wpkh') return;
89 |
90 | it(
91 | 'can (as P2WSH(' +
92 | k +
93 | ')) broadcast as an output, and be spent as an input',
94 | async () => {
95 | const p2wsh = bitcoin.payments.p2wsh({
96 | redeem: { output },
97 | network: NETWORK,
98 | });
99 | Object.assign(depends, { prevOutScriptType: 'p2wsh-' + k });
100 | await buildAndSign(
101 | depends,
102 | p2wsh.output,
103 | undefined,
104 | p2wsh.redeem!.output,
105 | );
106 | },
107 | );
108 |
109 | it(
110 | 'can (as P2SH(P2WSH(' +
111 | k +
112 | '))) broadcast as an output, and be spent as an input',
113 | async () => {
114 | const p2wsh = bitcoin.payments.p2wsh({
115 | redeem: { output },
116 | network: NETWORK,
117 | });
118 | const p2sh = bitcoin.payments.p2sh({
119 | redeem: { output: p2wsh.output },
120 | network: NETWORK,
121 | });
122 |
123 | Object.assign(depends, { prevOutScriptType: 'p2sh-p2wsh-' + k });
124 | await buildAndSign(
125 | depends,
126 | p2sh.output,
127 | p2sh.redeem!.output,
128 | p2wsh.redeem!.output,
129 | );
130 | },
131 | );
132 | });
133 | });
134 |
--------------------------------------------------------------------------------
/test/payments.spec.ts:
--------------------------------------------------------------------------------
1 | import * as assert from 'assert';
2 | import * as ecc from 'tiny-secp256k1';
3 | import { describe, it } from 'mocha';
4 | import { PaymentCreator } from '../src/payments';
5 | import * as u from './payments.utils';
6 | import { initEccLib } from '../src';
7 |
8 | ['embed', 'p2ms', 'p2pk', 'p2pkh', 'p2sh', 'p2wpkh', 'p2wsh', 'p2tr'].forEach(
9 | p => {
10 | describe(p, () => {
11 | beforeEach(() => {
12 | initEccLib(p === 'p2tr' ? ecc : undefined);
13 | });
14 | let fn: PaymentCreator;
15 | const payment = require('../src/payments/' + p);
16 | if (p === 'embed') {
17 | fn = payment.p2data;
18 | } else {
19 | fn = payment[p];
20 | }
21 |
22 | const fixtures = require('./fixtures/' + p);
23 |
24 | fixtures.valid.forEach((f: any) => {
25 | it(f.description + ' as expected', () => {
26 | const args = u.preform(f.arguments);
27 | const actual = fn(args, f.options);
28 |
29 | u.equate(actual, f.expected, f.arguments);
30 | });
31 |
32 | it(f.description + ' as expected (no validation)', () => {
33 | const args = u.preform(f.arguments);
34 | const actual = fn(
35 | args,
36 | Object.assign({}, f.options, {
37 | validate: false,
38 | }),
39 | );
40 |
41 | u.equate(actual, f.expected, f.arguments);
42 | });
43 | });
44 |
45 | fixtures.invalid.forEach((f: any) => {
46 | it(
47 | 'throws ' +
48 | f.exception +
49 | (f.description ? 'for ' + f.description : ''),
50 | () => {
51 | const args = u.preform(f.arguments);
52 |
53 | assert.throws(() => {
54 | fn(args, f.options);
55 | }, new RegExp(f.exception));
56 | },
57 | );
58 | });
59 |
60 | if (p === 'p2sh') {
61 | const p2wsh = require('../src/payments/p2wsh').p2wsh;
62 | const p2pk = require('../src/payments/p2pk').p2pk;
63 | it('properly assembles nested p2wsh with names', () => {
64 | const actual = fn({
65 | redeem: p2wsh({
66 | redeem: p2pk({
67 | pubkey: Buffer.from(
68 | '03e15819590382a9dd878f01e2f0cbce541564eb415e43b440472d883ecd283058',
69 | 'hex',
70 | ),
71 | }),
72 | }),
73 | });
74 | assert.strictEqual(
75 | actual.address,
76 | '3MGbrbye4ttNUXM8WAvBFRKry4fkS9fjuw',
77 | );
78 | assert.strictEqual(actual.name, 'p2sh-p2wsh-p2pk');
79 | assert.strictEqual(actual.redeem!.name, 'p2wsh-p2pk');
80 | assert.strictEqual(actual.redeem!.redeem!.name, 'p2pk');
81 | });
82 | }
83 |
84 | // cross-verify dynamically too
85 | if (!fixtures.dynamic) return;
86 | const { depends, details } = fixtures.dynamic;
87 |
88 | details.forEach((f: any) => {
89 | const detail = u.preform(f);
90 | const disabled: any = {};
91 | if (f.disabled)
92 | f.disabled.forEach((k: string) => {
93 | disabled[k] = true;
94 | });
95 |
96 | for (const key in depends) {
97 | if (key in disabled) continue;
98 | const dependencies = depends[key];
99 |
100 | dependencies.forEach((dependency: any) => {
101 | if (!Array.isArray(dependency)) dependency = [dependency];
102 |
103 | const args = {};
104 | dependency.forEach((d: any) => {
105 | u.from(d, detail, args);
106 | });
107 | const expected = u.from(key, detail);
108 |
109 | it(
110 | f.description +
111 | ', ' +
112 | key +
113 | ' derives from ' +
114 | JSON.stringify(dependency),
115 | () => {
116 | u.equate(fn(args), expected);
117 | },
118 | );
119 | });
120 | }
121 | });
122 | });
123 | },
124 | );
125 |
--------------------------------------------------------------------------------
/test/script_number.spec.ts:
--------------------------------------------------------------------------------
1 | import * as assert from 'assert';
2 | import { describe, it } from 'mocha';
3 | import * as scriptNumber from '../src/script_number';
4 | import * as fixtures from './fixtures/script_number.json';
5 |
6 | describe('script-number', () => {
7 | describe('decode', () => {
8 | fixtures.forEach(f => {
9 | it(f.hex + ' returns ' + f.number, () => {
10 | const actual = scriptNumber.decode(Buffer.from(f.hex, 'hex'), f.bytes);
11 |
12 | assert.strictEqual(actual, f.number);
13 | });
14 | });
15 | });
16 |
17 | describe('encode', () => {
18 | fixtures.forEach(f => {
19 | it(f.number + ' returns ' + f.hex, () => {
20 | const actual = scriptNumber.encode(f.number);
21 |
22 | assert.strictEqual(actual.toString('hex'), f.hex);
23 | });
24 | });
25 | });
26 | });
27 |
--------------------------------------------------------------------------------
/test/script_signature.spec.ts:
--------------------------------------------------------------------------------
1 | import * as assert from 'assert';
2 | import { describe, it } from 'mocha';
3 | import { signature as bscriptSig } from '../src/script';
4 | import * as fixtures from './fixtures/signature.json';
5 |
6 | describe('Script Signatures', () => {
7 | function fromRaw(signature: { r: string; s: string }): Buffer {
8 | return Buffer.concat(
9 | [Buffer.from(signature.r, 'hex'), Buffer.from(signature.s, 'hex')],
10 | 64,
11 | );
12 | }
13 |
14 | function toRaw(signature: Buffer): {
15 | r: string;
16 | s: string;
17 | } {
18 | return {
19 | r: signature.slice(0, 32).toString('hex'),
20 | s: signature.slice(32, 64).toString('hex'),
21 | };
22 | }
23 |
24 | describe('encode', () => {
25 | fixtures.valid.forEach(f => {
26 | it('encodes ' + f.hex, () => {
27 | const buffer = bscriptSig.encode(fromRaw(f.raw), f.hashType);
28 |
29 | assert.strictEqual(buffer.toString('hex'), f.hex);
30 | });
31 | });
32 |
33 | fixtures.invalid.forEach(f => {
34 | if (!f.raw) return;
35 |
36 | it('throws ' + f.exception, () => {
37 | const signature = fromRaw(f.raw);
38 |
39 | assert.throws(() => {
40 | bscriptSig.encode(signature, f.hashType);
41 | }, new RegExp(f.exception));
42 | });
43 | });
44 | });
45 |
46 | describe('decode', () => {
47 | fixtures.valid.forEach(f => {
48 | it('decodes ' + f.hex, () => {
49 | const decode = bscriptSig.decode(Buffer.from(f.hex, 'hex'));
50 |
51 | assert.deepStrictEqual(toRaw(decode.signature), f.raw);
52 | assert.strictEqual(decode.hashType, f.hashType);
53 | });
54 | });
55 |
56 | fixtures.invalid.forEach(f => {
57 | it('throws on ' + f.hex, () => {
58 | const buffer = Buffer.from(f.hex, 'hex');
59 |
60 | assert.throws(() => {
61 | bscriptSig.decode(buffer);
62 | }, new RegExp(f.exception));
63 | });
64 | });
65 | });
66 | });
67 |
--------------------------------------------------------------------------------
/test/ts-node-register.js:
--------------------------------------------------------------------------------
1 | // This file is required to run mocha tests on the TS files directly
2 |
3 | require("ts-node").register({
4 | project: "test/tsconfig.json",
5 | });
6 |
--------------------------------------------------------------------------------
/test/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2020",
4 | "module": "commonjs",
5 | "outDir": "../",
6 | "declaration": false,
7 | "rootDir": "../",
8 | "rootDirs": [
9 | "../src",
10 | "../types"
11 | ],
12 | "types": [
13 | "node",
14 | "mocha"
15 | ],
16 | "allowJs": false,
17 | "resolveJsonModule": true,
18 | "strict": true,
19 | "noImplicitAny": true,
20 | "strictNullChecks": true,
21 | "strictFunctionTypes": true,
22 | "strictBindCallApply": true,
23 | "strictPropertyInitialization": true,
24 | "noImplicitThis": true,
25 | "alwaysStrict": true,
26 | "esModuleInterop": false,
27 | "noUnusedLocals": true,
28 | "noUnusedParameters": true,
29 | "baseUrl": ".",
30 | "paths": {
31 | "../src/*": ["../ts_src/*"]
32 | }
33 | },
34 | "include": [
35 | "./**/*.ts"
36 | ],
37 | "exclude": [
38 | "../ts_src/**/*.ts"
39 | ]
40 | }
41 |
--------------------------------------------------------------------------------
/test/types.spec.ts:
--------------------------------------------------------------------------------
1 | import * as assert from 'assert';
2 | import { describe, it } from 'mocha';
3 | import * as types from '../src/types';
4 | const typeforce = require('typeforce');
5 |
6 | describe('types', () => {
7 | describe('Buffer Hash160/Hash256', () => {
8 | const buffer20byte = Buffer.alloc(20);
9 | const buffer32byte = Buffer.alloc(32);
10 |
11 | it('return true for valid size', () => {
12 | assert(types.Hash160bit(buffer20byte));
13 | assert(types.Hash256bit(buffer32byte));
14 | });
15 |
16 | it('return true for oneOf', () => {
17 | assert.doesNotThrow(() => {
18 | typeforce(
19 | types.oneOf(types.Hash160bit, types.Hash256bit),
20 | buffer32byte,
21 | );
22 | });
23 |
24 | assert.doesNotThrow(() => {
25 | typeforce(
26 | types.oneOf(types.Hash256bit, types.Hash160bit),
27 | buffer32byte,
28 | );
29 | });
30 | });
31 |
32 | it('throws for invalid size', () => {
33 | assert.throws(() => {
34 | types.Hash160bit(buffer32byte);
35 | }, /Expected Buffer\(Length: 20\), got Buffer\(Length: 32\)/);
36 |
37 | assert.throws(() => {
38 | types.Hash256bit(buffer20byte);
39 | }, /Expected Buffer\(Length: 32\), got Buffer\(Length: 20\)/);
40 | });
41 | });
42 |
43 | describe('Satoshi', () => {
44 | [
45 | { value: -1, result: false },
46 | { value: 0, result: true },
47 | { value: 1, result: true },
48 | { value: 20999999 * 1e8, result: true },
49 | { value: 21000000 * 1e8, result: true },
50 | { value: 21000001 * 1e8, result: false },
51 | ].forEach(f => {
52 | it('returns ' + f.result + ' for valid for ' + f.value, () => {
53 | assert.strictEqual(types.Satoshi(f.value), f.result);
54 | });
55 | });
56 | });
57 |
58 | describe('UInt31', () => {
59 | const UINT31_MAX = Math.pow(2, 31) - 1;
60 | it('return true for valid values', () => {
61 | assert.strictEqual(types.UInt31(0), true);
62 | assert.strictEqual(types.UInt31(1000), true);
63 | assert.strictEqual(types.UInt31(UINT31_MAX), true);
64 | });
65 |
66 | it('return false for negative values', () => {
67 | assert.strictEqual(types.UInt31(-1), false);
68 | assert.strictEqual(types.UInt31(-UINT31_MAX), false);
69 | });
70 |
71 | it(`return false for value > ${UINT31_MAX}`, () => {
72 | assert.strictEqual(types.UInt31(UINT31_MAX + 1), false);
73 | });
74 | });
75 |
76 | describe('BIP32Path', () => {
77 | it('return true for valid paths', () => {
78 | assert.strictEqual(types.BIP32Path("m/0'/0'"), true);
79 | assert.strictEqual(types.BIP32Path("m/0'/0"), true);
80 | assert.strictEqual(types.BIP32Path("m/0'/1'/2'/3/4'"), true);
81 | });
82 |
83 | it('return false for invalid paths', () => {
84 | assert.strictEqual(types.BIP32Path('m'), false);
85 | assert.strictEqual(types.BIP32Path("n/0'/0'"), false);
86 | assert.strictEqual(types.BIP32Path("m/0'/x"), false);
87 | });
88 |
89 | it('return "BIP32 derivation path" for JSON.strigify()', () => {
90 | const toJsonValue = JSON.stringify(types.BIP32Path);
91 | assert.equal(toJsonValue, '"BIP32 derivation path"');
92 | });
93 | });
94 | });
95 |
--------------------------------------------------------------------------------
/ts_src/bip66.ts:
--------------------------------------------------------------------------------
1 | // Reference https://github.com/bitcoin/bips/blob/master/bip-0066.mediawiki
2 | // Format: 0x30 [total-length] 0x02 [R-length] [R] 0x02 [S-length] [S]
3 | // NOTE: SIGHASH byte ignored AND restricted, truncate before use
4 |
5 | export function check(buffer: Buffer): boolean {
6 | if (buffer.length < 8) return false;
7 | if (buffer.length > 72) return false;
8 | if (buffer[0] !== 0x30) return false;
9 | if (buffer[1] !== buffer.length - 2) return false;
10 | if (buffer[2] !== 0x02) return false;
11 |
12 | const lenR = buffer[3];
13 | if (lenR === 0) return false;
14 | if (5 + lenR >= buffer.length) return false;
15 | if (buffer[4 + lenR] !== 0x02) return false;
16 |
17 | const lenS = buffer[5 + lenR];
18 | if (lenS === 0) return false;
19 | if (6 + lenR + lenS !== buffer.length) return false;
20 |
21 | if (buffer[4] & 0x80) return false;
22 | if (lenR > 1 && buffer[4] === 0x00 && !(buffer[5] & 0x80)) return false;
23 |
24 | if (buffer[lenR + 6] & 0x80) return false;
25 | if (lenS > 1 && buffer[lenR + 6] === 0x00 && !(buffer[lenR + 7] & 0x80))
26 | return false;
27 | return true;
28 | }
29 |
30 | export function decode(buffer: Buffer): { r: Buffer; s: Buffer } {
31 | if (buffer.length < 8) throw new Error('DER sequence length is too short');
32 | if (buffer.length > 72) throw new Error('DER sequence length is too long');
33 | if (buffer[0] !== 0x30) throw new Error('Expected DER sequence');
34 | if (buffer[1] !== buffer.length - 2)
35 | throw new Error('DER sequence length is invalid');
36 | if (buffer[2] !== 0x02) throw new Error('Expected DER integer');
37 |
38 | const lenR = buffer[3];
39 | if (lenR === 0) throw new Error('R length is zero');
40 | if (5 + lenR >= buffer.length) throw new Error('R length is too long');
41 | if (buffer[4 + lenR] !== 0x02) throw new Error('Expected DER integer (2)');
42 |
43 | const lenS = buffer[5 + lenR];
44 | if (lenS === 0) throw new Error('S length is zero');
45 | if (6 + lenR + lenS !== buffer.length) throw new Error('S length is invalid');
46 |
47 | if (buffer[4] & 0x80) throw new Error('R value is negative');
48 | if (lenR > 1 && buffer[4] === 0x00 && !(buffer[5] & 0x80))
49 | throw new Error('R value excessively padded');
50 |
51 | if (buffer[lenR + 6] & 0x80) throw new Error('S value is negative');
52 | if (lenS > 1 && buffer[lenR + 6] === 0x00 && !(buffer[lenR + 7] & 0x80))
53 | throw new Error('S value excessively padded');
54 |
55 | // non-BIP66 - extract R, S values
56 | return {
57 | r: buffer.slice(4, 4 + lenR),
58 | s: buffer.slice(6 + lenR),
59 | };
60 | }
61 |
62 | /*
63 | * Expects r and s to be positive DER integers.
64 | *
65 | * The DER format uses the most significant bit as a sign bit (& 0x80).
66 | * If the significant bit is set AND the integer is positive, a 0x00 is prepended.
67 | *
68 | * Examples:
69 | *
70 | * 0 => 0x00
71 | * 1 => 0x01
72 | * -1 => 0xff
73 | * 127 => 0x7f
74 | * -127 => 0x81
75 | * 128 => 0x0080
76 | * -128 => 0x80
77 | * 255 => 0x00ff
78 | * -255 => 0xff01
79 | * 16300 => 0x3fac
80 | * -16300 => 0xc054
81 | * 62300 => 0x00f35c
82 | * -62300 => 0xff0ca4
83 | */
84 | export function encode(r: Buffer, s: Buffer): Buffer {
85 | const lenR = r.length;
86 | const lenS = s.length;
87 | if (lenR === 0) throw new Error('R length is zero');
88 | if (lenS === 0) throw new Error('S length is zero');
89 | if (lenR > 33) throw new Error('R length is too long');
90 | if (lenS > 33) throw new Error('S length is too long');
91 | if (r[0] & 0x80) throw new Error('R value is negative');
92 | if (s[0] & 0x80) throw new Error('S value is negative');
93 | if (lenR > 1 && r[0] === 0x00 && !(r[1] & 0x80))
94 | throw new Error('R value excessively padded');
95 | if (lenS > 1 && s[0] === 0x00 && !(s[1] & 0x80))
96 | throw new Error('S value excessively padded');
97 |
98 | const signature = Buffer.allocUnsafe(6 + lenR + lenS);
99 |
100 | // 0x30 [total-length] 0x02 [R-length] [R] 0x02 [S-length] [S]
101 | signature[0] = 0x30;
102 | signature[1] = signature.length - 2;
103 | signature[2] = 0x02;
104 | signature[3] = r.length;
105 | r.copy(signature, 4);
106 | signature[4 + lenR] = 0x02;
107 | signature[5 + lenR] = s.length;
108 | s.copy(signature, 6 + lenR);
109 |
110 | return signature;
111 | }
112 |
--------------------------------------------------------------------------------
/ts_src/bufferutils.ts:
--------------------------------------------------------------------------------
1 | import * as types from './types';
2 | const { typeforce } = types;
3 | import * as varuint from 'varuint-bitcoin';
4 | export { varuint };
5 |
6 | // https://github.com/feross/buffer/blob/master/index.js#L1127
7 | function verifuint(value: number, max: number): void {
8 | if (typeof value !== 'number')
9 | throw new Error('cannot write a non-number as a number');
10 | if (value < 0)
11 | throw new Error('specified a negative value for writing an unsigned value');
12 | if (value > max) throw new Error('RangeError: value out of range');
13 | if (Math.floor(value) !== value)
14 | throw new Error('value has a fractional component');
15 | }
16 |
17 | export function readUInt64LE(buffer: Buffer, offset: number): number {
18 | const a = buffer.readUInt32LE(offset);
19 | let b = buffer.readUInt32LE(offset + 4);
20 | b *= 0x100000000;
21 |
22 | verifuint(b + a, 0x001fffffffffffff);
23 | return b + a;
24 | }
25 |
26 | export function writeUInt64LE(
27 | buffer: Buffer,
28 | value: number,
29 | offset: number,
30 | ): number {
31 | verifuint(value, 0x001fffffffffffff);
32 |
33 | buffer.writeInt32LE(value & -1, offset);
34 | buffer.writeUInt32LE(Math.floor(value / 0x100000000), offset + 4);
35 | return offset + 8;
36 | }
37 |
38 | export function reverseBuffer(buffer: Buffer): Buffer {
39 | if (buffer.length < 1) return buffer;
40 | let j = buffer.length - 1;
41 | let tmp = 0;
42 | for (let i = 0; i < buffer.length / 2; i++) {
43 | tmp = buffer[i];
44 | buffer[i] = buffer[j];
45 | buffer[j] = tmp;
46 | j--;
47 | }
48 | return buffer;
49 | }
50 |
51 | export function cloneBuffer(buffer: Buffer): Buffer {
52 | const clone = Buffer.allocUnsafe(buffer.length);
53 | buffer.copy(clone);
54 | return clone;
55 | }
56 |
57 | /**
58 | * Helper class for serialization of bitcoin data types into a pre-allocated buffer.
59 | */
60 | export class BufferWriter {
61 | static withCapacity(size: number): BufferWriter {
62 | return new BufferWriter(Buffer.alloc(size));
63 | }
64 |
65 | constructor(public buffer: Buffer, public offset: number = 0) {
66 | typeforce(types.tuple(types.Buffer, types.UInt32), [buffer, offset]);
67 | }
68 |
69 | writeUInt8(i: number): void {
70 | this.offset = this.buffer.writeUInt8(i, this.offset);
71 | }
72 |
73 | writeInt32(i: number): void {
74 | this.offset = this.buffer.writeInt32LE(i, this.offset);
75 | }
76 |
77 | writeUInt32(i: number): void {
78 | this.offset = this.buffer.writeUInt32LE(i, this.offset);
79 | }
80 |
81 | writeUInt64(i: number): void {
82 | this.offset = writeUInt64LE(this.buffer, i, this.offset);
83 | }
84 |
85 | writeVarInt(i: number): void {
86 | varuint.encode(i, this.buffer, this.offset);
87 | this.offset += varuint.encode.bytes;
88 | }
89 |
90 | writeSlice(slice: Buffer): void {
91 | if (this.buffer.length < this.offset + slice.length) {
92 | throw new Error('Cannot write slice out of bounds');
93 | }
94 | this.offset += slice.copy(this.buffer, this.offset);
95 | }
96 |
97 | writeVarSlice(slice: Buffer): void {
98 | this.writeVarInt(slice.length);
99 | this.writeSlice(slice);
100 | }
101 |
102 | writeVector(vector: Buffer[]): void {
103 | this.writeVarInt(vector.length);
104 | vector.forEach((buf: Buffer) => this.writeVarSlice(buf));
105 | }
106 |
107 | end(): Buffer {
108 | if (this.buffer.length === this.offset) {
109 | return this.buffer;
110 | }
111 | throw new Error(`buffer size ${this.buffer.length}, offset ${this.offset}`);
112 | }
113 | }
114 |
115 | /**
116 | * Helper class for reading of bitcoin data types from a buffer.
117 | */
118 | export class BufferReader {
119 | constructor(public buffer: Buffer, public offset: number = 0) {
120 | typeforce(types.tuple(types.Buffer, types.UInt32), [buffer, offset]);
121 | }
122 |
123 | readUInt8(): number {
124 | const result = this.buffer.readUInt8(this.offset);
125 | this.offset++;
126 | return result;
127 | }
128 |
129 | readInt32(): number {
130 | const result = this.buffer.readInt32LE(this.offset);
131 | this.offset += 4;
132 | return result;
133 | }
134 |
135 | readUInt32(): number {
136 | const result = this.buffer.readUInt32LE(this.offset);
137 | this.offset += 4;
138 | return result;
139 | }
140 |
141 | readUInt64(): number {
142 | const result = readUInt64LE(this.buffer, this.offset);
143 | this.offset += 8;
144 | return result;
145 | }
146 |
147 | readVarInt(): number {
148 | const vi = varuint.decode(this.buffer, this.offset);
149 | this.offset += varuint.decode.bytes;
150 | return vi;
151 | }
152 |
153 | readSlice(n: number): Buffer {
154 | if (this.buffer.length < this.offset + n) {
155 | throw new Error('Cannot read slice out of bounds');
156 | }
157 | const result = this.buffer.slice(this.offset, this.offset + n);
158 | this.offset += n;
159 | return result;
160 | }
161 |
162 | readVarSlice(): Buffer {
163 | return this.readSlice(this.readVarInt());
164 | }
165 |
166 | readVector(): Buffer[] {
167 | const count = this.readVarInt();
168 | const vector: Buffer[] = [];
169 | for (let i = 0; i < count; i++) vector.push(this.readVarSlice());
170 | return vector;
171 | }
172 | }
173 |
--------------------------------------------------------------------------------
/ts_src/crypto.ts:
--------------------------------------------------------------------------------
1 | import { ripemd160 as _ripemd160 } from '@noble/hashes/ripemd160';
2 | import { sha1 as _sha1 } from '@noble/hashes/sha1';
3 | import { sha256 as _sha256 } from '@noble/hashes/sha256';
4 |
5 | export function ripemd160(buffer: Buffer): Buffer {
6 | return Buffer.from(_ripemd160(Uint8Array.from(buffer)));
7 | }
8 |
9 | export function sha1(buffer: Buffer): Buffer {
10 | return Buffer.from(_sha1(Uint8Array.from(buffer)));
11 | }
12 |
13 | export function sha256(buffer: Buffer): Buffer {
14 | return Buffer.from(_sha256(Uint8Array.from(buffer)));
15 | }
16 |
17 | export function hash160(buffer: Buffer): Buffer {
18 | return Buffer.from(_ripemd160(_sha256(Uint8Array.from(buffer))));
19 | }
20 |
21 | export function hash256(buffer: Buffer): Buffer {
22 | return Buffer.from(_sha256(_sha256(Uint8Array.from(buffer))));
23 | }
24 |
25 | export const TAGS = [
26 | 'BIP0340/challenge',
27 | 'BIP0340/aux',
28 | 'BIP0340/nonce',
29 | 'TapLeaf',
30 | 'TapBranch',
31 | 'TapSighash',
32 | 'TapTweak',
33 | 'KeyAgg list',
34 | 'KeyAgg coefficient',
35 | ] as const;
36 | export type TaggedHashPrefix = typeof TAGS[number];
37 | type TaggedHashPrefixes = {
38 | [key in TaggedHashPrefix]: Buffer;
39 | };
40 | /** An object mapping tags to their tagged hash prefix of [SHA256(tag) | SHA256(tag)] */
41 | export const TAGGED_HASH_PREFIXES: TaggedHashPrefixes = {
42 | 'BIP0340/challenge': Buffer.from([
43 | 123, 181, 45, 122, 159, 239, 88, 50, 62, 177, 191, 122, 64, 125, 179, 130,
44 | 210, 243, 242, 216, 27, 177, 34, 79, 73, 254, 81, 143, 109, 72, 211, 124,
45 | 123, 181, 45, 122, 159, 239, 88, 50, 62, 177, 191, 122, 64, 125, 179, 130,
46 | 210, 243, 242, 216, 27, 177, 34, 79, 73, 254, 81, 143, 109, 72, 211, 124,
47 | ]),
48 | 'BIP0340/aux': Buffer.from([
49 | 241, 239, 78, 94, 192, 99, 202, 218, 109, 148, 202, 250, 157, 152, 126, 160,
50 | 105, 38, 88, 57, 236, 193, 31, 151, 45, 119, 165, 46, 216, 193, 204, 144,
51 | 241, 239, 78, 94, 192, 99, 202, 218, 109, 148, 202, 250, 157, 152, 126, 160,
52 | 105, 38, 88, 57, 236, 193, 31, 151, 45, 119, 165, 46, 216, 193, 204, 144,
53 | ]),
54 | 'BIP0340/nonce': Buffer.from([
55 | 7, 73, 119, 52, 167, 155, 203, 53, 91, 155, 140, 125, 3, 79, 18, 28, 244,
56 | 52, 215, 62, 247, 45, 218, 25, 135, 0, 97, 251, 82, 191, 235, 47, 7, 73,
57 | 119, 52, 167, 155, 203, 53, 91, 155, 140, 125, 3, 79, 18, 28, 244, 52, 215,
58 | 62, 247, 45, 218, 25, 135, 0, 97, 251, 82, 191, 235, 47,
59 | ]),
60 | TapLeaf: Buffer.from([
61 | 174, 234, 143, 220, 66, 8, 152, 49, 5, 115, 75, 88, 8, 29, 30, 38, 56, 211,
62 | 95, 28, 181, 64, 8, 212, 211, 87, 202, 3, 190, 120, 233, 238, 174, 234, 143,
63 | 220, 66, 8, 152, 49, 5, 115, 75, 88, 8, 29, 30, 38, 56, 211, 95, 28, 181,
64 | 64, 8, 212, 211, 87, 202, 3, 190, 120, 233, 238,
65 | ]),
66 | TapBranch: Buffer.from([
67 | 25, 65, 161, 242, 229, 110, 185, 95, 162, 169, 241, 148, 190, 92, 1, 247,
68 | 33, 111, 51, 237, 130, 176, 145, 70, 52, 144, 208, 91, 245, 22, 160, 21, 25,
69 | 65, 161, 242, 229, 110, 185, 95, 162, 169, 241, 148, 190, 92, 1, 247, 33,
70 | 111, 51, 237, 130, 176, 145, 70, 52, 144, 208, 91, 245, 22, 160, 21,
71 | ]),
72 | TapSighash: Buffer.from([
73 | 244, 10, 72, 223, 75, 42, 112, 200, 180, 146, 75, 242, 101, 70, 97, 237, 61,
74 | 149, 253, 102, 163, 19, 235, 135, 35, 117, 151, 198, 40, 228, 160, 49, 244,
75 | 10, 72, 223, 75, 42, 112, 200, 180, 146, 75, 242, 101, 70, 97, 237, 61, 149,
76 | 253, 102, 163, 19, 235, 135, 35, 117, 151, 198, 40, 228, 160, 49,
77 | ]),
78 | TapTweak: Buffer.from([
79 | 232, 15, 225, 99, 156, 156, 160, 80, 227, 175, 27, 57, 193, 67, 198, 62, 66,
80 | 156, 188, 235, 21, 217, 64, 251, 181, 197, 161, 244, 175, 87, 197, 233, 232,
81 | 15, 225, 99, 156, 156, 160, 80, 227, 175, 27, 57, 193, 67, 198, 62, 66, 156,
82 | 188, 235, 21, 217, 64, 251, 181, 197, 161, 244, 175, 87, 197, 233,
83 | ]),
84 | 'KeyAgg list': Buffer.from([
85 | 72, 28, 151, 28, 60, 11, 70, 215, 240, 178, 117, 174, 89, 141, 78, 44, 126,
86 | 215, 49, 156, 89, 74, 92, 110, 199, 158, 160, 212, 153, 2, 148, 240, 72, 28,
87 | 151, 28, 60, 11, 70, 215, 240, 178, 117, 174, 89, 141, 78, 44, 126, 215, 49,
88 | 156, 89, 74, 92, 110, 199, 158, 160, 212, 153, 2, 148, 240,
89 | ]),
90 | 'KeyAgg coefficient': Buffer.from([
91 | 191, 201, 4, 3, 77, 28, 136, 232, 200, 14, 34, 229, 61, 36, 86, 109, 100,
92 | 130, 78, 214, 66, 114, 129, 192, 145, 0, 249, 77, 205, 82, 201, 129, 191,
93 | 201, 4, 3, 77, 28, 136, 232, 200, 14, 34, 229, 61, 36, 86, 109, 100, 130,
94 | 78, 214, 66, 114, 129, 192, 145, 0, 249, 77, 205, 82, 201, 129,
95 | ]),
96 | };
97 |
98 | export function taggedHash(prefix: TaggedHashPrefix, data: Buffer): Buffer {
99 | return sha256(Buffer.concat([TAGGED_HASH_PREFIXES[prefix], data]));
100 | }
101 |
--------------------------------------------------------------------------------
/ts_src/ecc_lib.ts:
--------------------------------------------------------------------------------
1 | import { TinySecp256k1Interface } from './types';
2 |
3 | const _ECCLIB_CACHE: { eccLib?: TinySecp256k1Interface } = {};
4 |
5 | export function initEccLib(eccLib: TinySecp256k1Interface | undefined): void {
6 | if (!eccLib) {
7 | // allow clearing the library
8 | _ECCLIB_CACHE.eccLib = eccLib;
9 | } else if (eccLib !== _ECCLIB_CACHE.eccLib) {
10 | // new instance, verify it
11 | verifyEcc(eccLib!);
12 | _ECCLIB_CACHE.eccLib = eccLib;
13 | }
14 | }
15 |
16 | export function getEccLib(): TinySecp256k1Interface {
17 | if (!_ECCLIB_CACHE.eccLib)
18 | throw new Error(
19 | 'No ECC Library provided. You must call initEccLib() with a valid TinySecp256k1Interface instance',
20 | );
21 | return _ECCLIB_CACHE.eccLib;
22 | }
23 |
24 | const h = (hex: string): Buffer => Buffer.from(hex, 'hex');
25 |
26 | function verifyEcc(ecc: TinySecp256k1Interface): void {
27 | assert(typeof ecc.isXOnlyPoint === 'function');
28 | assert(
29 | ecc.isXOnlyPoint(
30 | h('79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798'),
31 | ),
32 | );
33 | assert(
34 | ecc.isXOnlyPoint(
35 | h('fffffffffffffffffffffffffffffffffffffffffffffffffffffffeeffffc2e'),
36 | ),
37 | );
38 | assert(
39 | ecc.isXOnlyPoint(
40 | h('f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9'),
41 | ),
42 | );
43 | assert(
44 | ecc.isXOnlyPoint(
45 | h('0000000000000000000000000000000000000000000000000000000000000001'),
46 | ),
47 | );
48 | assert(
49 | !ecc.isXOnlyPoint(
50 | h('0000000000000000000000000000000000000000000000000000000000000000'),
51 | ),
52 | );
53 | assert(
54 | !ecc.isXOnlyPoint(
55 | h('fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f'),
56 | ),
57 | );
58 |
59 | assert(typeof ecc.xOnlyPointAddTweak === 'function');
60 | tweakAddVectors.forEach(t => {
61 | const r = ecc.xOnlyPointAddTweak(h(t.pubkey), h(t.tweak));
62 | if (t.result === null) {
63 | assert(r === null);
64 | } else {
65 | assert(r !== null);
66 | assert(r!.parity === t.parity);
67 | assert(Buffer.from(r!.xOnlyPubkey).equals(h(t.result)));
68 | }
69 | });
70 | }
71 |
72 | function assert(bool: boolean): void {
73 | if (!bool) throw new Error('ecc library invalid');
74 | }
75 |
76 | const tweakAddVectors = [
77 | {
78 | pubkey: '79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798',
79 | tweak: 'fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140',
80 | parity: -1,
81 | result: null,
82 | },
83 | {
84 | pubkey: '1617d38ed8d8657da4d4761e8057bc396ea9e4b9d29776d4be096016dbd2509b',
85 | tweak: 'a8397a935f0dfceba6ba9618f6451ef4d80637abf4e6af2669fbc9de6a8fd2ac',
86 | parity: 1,
87 | result: 'e478f99dab91052ab39a33ea35fd5e6e4933f4d28023cd597c9a1f6760346adf',
88 | },
89 | {
90 | pubkey: '2c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e668680991',
91 | tweak: '823c3cd2142744b075a87eade7e1b8678ba308d566226a0056ca2b7a76f86b47',
92 | parity: 0,
93 | result: '9534f8dc8c6deda2dc007655981c78b49c5d96c778fbf363462a11ec9dfd948c',
94 | },
95 | ];
96 |
--------------------------------------------------------------------------------
/ts_src/index.ts:
--------------------------------------------------------------------------------
1 | import * as address from './address';
2 | import * as crypto from './crypto';
3 | import * as networks from './networks';
4 | import * as payments from './payments';
5 | import * as script from './script';
6 |
7 | export { address, crypto, networks, payments, script };
8 |
9 | export { Block } from './block';
10 | export { TaggedHashPrefix } from './crypto';
11 | export {
12 | Psbt,
13 | PsbtTxInput,
14 | PsbtTxOutput,
15 | Signer,
16 | SignerAsync,
17 | HDSigner,
18 | HDSignerAsync,
19 | } from './psbt';
20 | export { OPS as opcodes } from './ops';
21 | export { Transaction } from './transaction';
22 |
23 | export { Network } from './networks';
24 | export {
25 | Payment,
26 | PaymentCreator,
27 | PaymentOpts,
28 | Stack,
29 | StackElement,
30 | } from './payments';
31 | export { Input as TxInput, Output as TxOutput } from './transaction';
32 | export { initEccLib } from './ecc_lib';
33 |
--------------------------------------------------------------------------------
/ts_src/merkle.ts:
--------------------------------------------------------------------------------
1 | export function fastMerkleRoot(
2 | values: Buffer[],
3 | digestFn: (b: Buffer) => Buffer,
4 | ): Buffer {
5 | if (!Array.isArray(values)) throw TypeError('Expected values Array');
6 | if (typeof digestFn !== 'function')
7 | throw TypeError('Expected digest Function');
8 |
9 | let length = values.length;
10 | const results = values.concat();
11 |
12 | while (length > 1) {
13 | let j = 0;
14 |
15 | for (let i = 0; i < length; i += 2, ++j) {
16 | const left = results[i];
17 | const right = i + 1 === length ? left : results[i + 1];
18 | const data = Buffer.concat([left, right]);
19 |
20 | results[j] = digestFn(data);
21 | }
22 |
23 | length = j;
24 | }
25 |
26 | return results[0];
27 | }
28 |
--------------------------------------------------------------------------------
/ts_src/networks.ts:
--------------------------------------------------------------------------------
1 | // https://en.bitcoin.it/wiki/List_of_address_prefixes
2 | // Dogecoin BIP32 is a proposed standard: https://bitcointalk.org/index.php?topic=409731
3 | export interface Network {
4 | messagePrefix: string;
5 | bech32: string;
6 | bip32: Bip32;
7 | pubKeyHash: number;
8 | scriptHash: number;
9 | wif: number;
10 | }
11 |
12 | interface Bip32 {
13 | public: number;
14 | private: number;
15 | }
16 |
17 | export const bitcoin: Network = {
18 | messagePrefix: '\x18Bitcoin Signed Message:\n',
19 | bech32: 'bc',
20 | bip32: {
21 | public: 0x0488b21e,
22 | private: 0x0488ade4,
23 | },
24 | pubKeyHash: 0x00,
25 | scriptHash: 0x05,
26 | wif: 0x80,
27 | };
28 | export const regtest: Network = {
29 | messagePrefix: '\x18Bitcoin Signed Message:\n',
30 | bech32: 'bcrt',
31 | bip32: {
32 | public: 0x043587cf,
33 | private: 0x04358394,
34 | },
35 | pubKeyHash: 0x6f,
36 | scriptHash: 0xc4,
37 | wif: 0xef,
38 | };
39 | export const testnet: Network = {
40 | messagePrefix: '\x18Bitcoin Signed Message:\n',
41 | bech32: 'tb',
42 | bip32: {
43 | public: 0x043587cf,
44 | private: 0x04358394,
45 | },
46 | pubKeyHash: 0x6f,
47 | scriptHash: 0xc4,
48 | wif: 0xef,
49 | };
50 |
--------------------------------------------------------------------------------
/ts_src/ops.ts:
--------------------------------------------------------------------------------
1 | const OPS: { [key: string]: number } = {
2 | OP_FALSE: 0,
3 | OP_0: 0,
4 | OP_PUSHDATA1: 76,
5 | OP_PUSHDATA2: 77,
6 | OP_PUSHDATA4: 78,
7 | OP_1NEGATE: 79,
8 | OP_RESERVED: 80,
9 | OP_TRUE: 81,
10 | OP_1: 81,
11 | OP_2: 82,
12 | OP_3: 83,
13 | OP_4: 84,
14 | OP_5: 85,
15 | OP_6: 86,
16 | OP_7: 87,
17 | OP_8: 88,
18 | OP_9: 89,
19 | OP_10: 90,
20 | OP_11: 91,
21 | OP_12: 92,
22 | OP_13: 93,
23 | OP_14: 94,
24 | OP_15: 95,
25 | OP_16: 96,
26 |
27 | OP_NOP: 97,
28 | OP_VER: 98,
29 | OP_IF: 99,
30 | OP_NOTIF: 100,
31 | OP_VERIF: 101,
32 | OP_VERNOTIF: 102,
33 | OP_ELSE: 103,
34 | OP_ENDIF: 104,
35 | OP_VERIFY: 105,
36 | OP_RETURN: 106,
37 |
38 | OP_TOALTSTACK: 107,
39 | OP_FROMALTSTACK: 108,
40 | OP_2DROP: 109,
41 | OP_2DUP: 110,
42 | OP_3DUP: 111,
43 | OP_2OVER: 112,
44 | OP_2ROT: 113,
45 | OP_2SWAP: 114,
46 | OP_IFDUP: 115,
47 | OP_DEPTH: 116,
48 | OP_DROP: 117,
49 | OP_DUP: 118,
50 | OP_NIP: 119,
51 | OP_OVER: 120,
52 | OP_PICK: 121,
53 | OP_ROLL: 122,
54 | OP_ROT: 123,
55 | OP_SWAP: 124,
56 | OP_TUCK: 125,
57 |
58 | OP_CAT: 126,
59 | OP_SUBSTR: 127,
60 | OP_LEFT: 128,
61 | OP_RIGHT: 129,
62 | OP_SIZE: 130,
63 |
64 | OP_INVERT: 131,
65 | OP_AND: 132,
66 | OP_OR: 133,
67 | OP_XOR: 134,
68 | OP_EQUAL: 135,
69 | OP_EQUALVERIFY: 136,
70 | OP_RESERVED1: 137,
71 | OP_RESERVED2: 138,
72 |
73 | OP_1ADD: 139,
74 | OP_1SUB: 140,
75 | OP_2MUL: 141,
76 | OP_2DIV: 142,
77 | OP_NEGATE: 143,
78 | OP_ABS: 144,
79 | OP_NOT: 145,
80 | OP_0NOTEQUAL: 146,
81 | OP_ADD: 147,
82 | OP_SUB: 148,
83 | OP_MUL: 149,
84 | OP_DIV: 150,
85 | OP_MOD: 151,
86 | OP_LSHIFT: 152,
87 | OP_RSHIFT: 153,
88 |
89 | OP_BOOLAND: 154,
90 | OP_BOOLOR: 155,
91 | OP_NUMEQUAL: 156,
92 | OP_NUMEQUALVERIFY: 157,
93 | OP_NUMNOTEQUAL: 158,
94 | OP_LESSTHAN: 159,
95 | OP_GREATERTHAN: 160,
96 | OP_LESSTHANOREQUAL: 161,
97 | OP_GREATERTHANOREQUAL: 162,
98 | OP_MIN: 163,
99 | OP_MAX: 164,
100 |
101 | OP_WITHIN: 165,
102 |
103 | OP_RIPEMD160: 166,
104 | OP_SHA1: 167,
105 | OP_SHA256: 168,
106 | OP_HASH160: 169,
107 | OP_HASH256: 170,
108 | OP_CODESEPARATOR: 171,
109 | OP_CHECKSIG: 172,
110 | OP_CHECKSIGVERIFY: 173,
111 | OP_CHECKMULTISIG: 174,
112 | OP_CHECKMULTISIGVERIFY: 175,
113 |
114 | OP_NOP1: 176,
115 |
116 | OP_NOP2: 177,
117 | OP_CHECKLOCKTIMEVERIFY: 177,
118 |
119 | OP_NOP3: 178,
120 | OP_CHECKSEQUENCEVERIFY: 178,
121 |
122 | OP_NOP4: 179,
123 | OP_NOP5: 180,
124 | OP_NOP6: 181,
125 | OP_NOP7: 182,
126 | OP_NOP8: 183,
127 | OP_NOP9: 184,
128 | OP_NOP10: 185,
129 |
130 | OP_CHECKSIGADD: 186,
131 |
132 | OP_PUBKEYHASH: 253,
133 | OP_PUBKEY: 254,
134 | OP_INVALIDOPCODE: 255,
135 | };
136 |
137 | const REVERSE_OPS: { [key: number]: string } = {};
138 | for (const op of Object.keys(OPS)) {
139 | const code = OPS[op];
140 | REVERSE_OPS[code] = op;
141 | }
142 |
143 | export { OPS, REVERSE_OPS };
144 |
--------------------------------------------------------------------------------
/ts_src/payments/bip341.ts:
--------------------------------------------------------------------------------
1 | import { Buffer as NBuffer } from 'buffer';
2 | import { getEccLib } from '../ecc_lib';
3 | import * as bcrypto from '../crypto';
4 |
5 | import { varuint } from '../bufferutils';
6 | import { Tapleaf, Taptree, isTapleaf } from '../types';
7 |
8 | export const LEAF_VERSION_TAPSCRIPT = 0xc0;
9 | export const MAX_TAPTREE_DEPTH = 128;
10 |
11 | interface HashLeaf {
12 | hash: Buffer;
13 | }
14 |
15 | interface HashBranch {
16 | hash: Buffer;
17 | left: HashTree;
18 | right: HashTree;
19 | }
20 |
21 | interface TweakedPublicKey {
22 | parity: number;
23 | x: Buffer;
24 | }
25 |
26 | const isHashBranch = (ht: HashTree): ht is HashBranch =>
27 | 'left' in ht && 'right' in ht;
28 |
29 | /**
30 | * Binary tree representing leaf, branch, and root node hashes of a Taptree.
31 | * Each node contains a hash, and potentially left and right branch hashes.
32 | * This tree is used for 2 purposes: Providing the root hash for tweaking,
33 | * and calculating merkle inclusion proofs when constructing a control block.
34 | */
35 | export type HashTree = HashLeaf | HashBranch;
36 |
37 | export function rootHashFromPath(
38 | controlBlock: Buffer,
39 | leafHash: Buffer,
40 | ): Buffer {
41 | if (controlBlock.length < 33)
42 | throw new TypeError(
43 | `The control-block length is too small. Got ${controlBlock.length}, expected min 33.`,
44 | );
45 | const m = (controlBlock.length - 33) / 32;
46 |
47 | let kj = leafHash;
48 | for (let j = 0; j < m; j++) {
49 | const ej = controlBlock.slice(33 + 32 * j, 65 + 32 * j);
50 | if (kj.compare(ej) < 0) {
51 | kj = tapBranchHash(kj, ej);
52 | } else {
53 | kj = tapBranchHash(ej, kj);
54 | }
55 | }
56 |
57 | return kj;
58 | }
59 |
60 | /**
61 | * Build a hash tree of merkle nodes from the scripts binary tree.
62 | * @param scriptTree - the tree of scripts to pairwise hash.
63 | */
64 | export function toHashTree(scriptTree: Taptree): HashTree {
65 | if (isTapleaf(scriptTree)) return { hash: tapleafHash(scriptTree) };
66 |
67 | const hashes = [toHashTree(scriptTree[0]), toHashTree(scriptTree[1])];
68 | hashes.sort((a, b) => a.hash.compare(b.hash));
69 | const [left, right] = hashes;
70 |
71 | return {
72 | hash: tapBranchHash(left.hash, right.hash),
73 | left,
74 | right,
75 | };
76 | }
77 |
78 | /**
79 | * Given a HashTree, finds the path from a particular hash to the root.
80 | * @param node - the root of the tree
81 | * @param hash - the hash to search for
82 | * @returns - array of sibling hashes, from leaf (inclusive) to root
83 | * (exclusive) needed to prove inclusion of the specified hash. undefined if no
84 | * path is found
85 | */
86 | export function findScriptPath(
87 | node: HashTree,
88 | hash: Buffer,
89 | ): Buffer[] | undefined {
90 | if (isHashBranch(node)) {
91 | const leftPath = findScriptPath(node.left, hash);
92 | if (leftPath !== undefined) return [...leftPath, node.right.hash];
93 |
94 | const rightPath = findScriptPath(node.right, hash);
95 | if (rightPath !== undefined) return [...rightPath, node.left.hash];
96 | } else if (node.hash.equals(hash)) {
97 | return [];
98 | }
99 |
100 | return undefined;
101 | }
102 |
103 | export function tapleafHash(leaf: Tapleaf): Buffer {
104 | const version = leaf.version || LEAF_VERSION_TAPSCRIPT;
105 | return bcrypto.taggedHash(
106 | 'TapLeaf',
107 | NBuffer.concat([NBuffer.from([version]), serializeScript(leaf.output)]),
108 | );
109 | }
110 |
111 | export function tapTweakHash(pubKey: Buffer, h: Buffer | undefined): Buffer {
112 | return bcrypto.taggedHash(
113 | 'TapTweak',
114 | NBuffer.concat(h ? [pubKey, h] : [pubKey]),
115 | );
116 | }
117 |
118 | export function tweakKey(
119 | pubKey: Buffer,
120 | h: Buffer | undefined,
121 | ): TweakedPublicKey | null {
122 | if (!NBuffer.isBuffer(pubKey)) return null;
123 | if (pubKey.length !== 32) return null;
124 | if (h && h.length !== 32) return null;
125 |
126 | const tweakHash = tapTweakHash(pubKey, h);
127 |
128 | const res = getEccLib().xOnlyPointAddTweak(pubKey, tweakHash);
129 | if (!res || res.xOnlyPubkey === null) return null;
130 |
131 | return {
132 | parity: res.parity,
133 | x: NBuffer.from(res.xOnlyPubkey),
134 | };
135 | }
136 |
137 | function tapBranchHash(a: Buffer, b: Buffer): Buffer {
138 | return bcrypto.taggedHash('TapBranch', NBuffer.concat([a, b]));
139 | }
140 |
141 | function serializeScript(s: Buffer): Buffer {
142 | const varintLen = varuint.encodingLength(s.length);
143 | const buffer = NBuffer.allocUnsafe(varintLen); // better
144 | varuint.encode(s.length, buffer);
145 | return NBuffer.concat([buffer, s]);
146 | }
147 |
--------------------------------------------------------------------------------
/ts_src/payments/embed.ts:
--------------------------------------------------------------------------------
1 | import { bitcoin as BITCOIN_NETWORK } from '../networks';
2 | import * as bscript from '../script';
3 | import { typeforce as typef } from '../types';
4 | import { Payment, PaymentOpts, Stack } from './index';
5 | import * as lazy from './lazy';
6 |
7 | const OPS = bscript.OPS;
8 |
9 | function stacksEqual(a: Buffer[], b: Buffer[]): boolean {
10 | if (a.length !== b.length) return false;
11 |
12 | return a.every((x, i) => {
13 | return x.equals(b[i]);
14 | });
15 | }
16 |
17 | // output: OP_RETURN ...
18 | export function p2data(a: Payment, opts?: PaymentOpts): Payment {
19 | if (!a.data && !a.output) throw new TypeError('Not enough data');
20 | opts = Object.assign({ validate: true }, opts || {});
21 |
22 | typef(
23 | {
24 | network: typef.maybe(typef.Object),
25 | output: typef.maybe(typef.Buffer),
26 | data: typef.maybe(typef.arrayOf(typef.Buffer)),
27 | },
28 | a,
29 | );
30 |
31 | const network = a.network || BITCOIN_NETWORK;
32 | const o = { name: 'embed', network } as Payment;
33 |
34 | lazy.prop(o, 'output', () => {
35 | if (!a.data) return;
36 | return bscript.compile(([OPS.OP_RETURN] as Stack).concat(a.data));
37 | });
38 | lazy.prop(o, 'data', () => {
39 | if (!a.output) return;
40 | return bscript.decompile(a.output)!.slice(1);
41 | });
42 |
43 | // extended validation
44 | if (opts.validate) {
45 | if (a.output) {
46 | const chunks = bscript.decompile(a.output);
47 | if (chunks![0] !== OPS.OP_RETURN)
48 | throw new TypeError('Output is invalid');
49 | if (!chunks!.slice(1).every(typef.Buffer))
50 | throw new TypeError('Output is invalid');
51 |
52 | if (a.data && !stacksEqual(a.data, o.data as Buffer[]))
53 | throw new TypeError('Data mismatch');
54 | }
55 | }
56 |
57 | return Object.assign(o, a);
58 | }
59 |
--------------------------------------------------------------------------------
/ts_src/payments/index.ts:
--------------------------------------------------------------------------------
1 | import { Network } from '../networks';
2 | import { Taptree } from '../types';
3 | import { p2data as embed } from './embed';
4 | import { p2ms } from './p2ms';
5 | import { p2pk } from './p2pk';
6 | import { p2pkh } from './p2pkh';
7 | import { p2sh } from './p2sh';
8 | import { p2wpkh } from './p2wpkh';
9 | import { p2wsh } from './p2wsh';
10 | import { p2tr } from './p2tr';
11 |
12 | export interface Payment {
13 | name?: string;
14 | network?: Network;
15 | output?: Buffer;
16 | data?: Buffer[];
17 | m?: number;
18 | n?: number;
19 | pubkeys?: Buffer[];
20 | input?: Buffer;
21 | signatures?: Buffer[];
22 | internalPubkey?: Buffer;
23 | pubkey?: Buffer;
24 | signature?: Buffer;
25 | address?: string;
26 | hash?: Buffer;
27 | redeem?: Payment;
28 | redeemVersion?: number;
29 | scriptTree?: Taptree;
30 | witness?: Buffer[];
31 | }
32 |
33 | export type PaymentCreator = (a: Payment, opts?: PaymentOpts) => Payment;
34 |
35 | export type PaymentFunction = () => Payment;
36 |
37 | export interface PaymentOpts {
38 | validate?: boolean;
39 | allowIncomplete?: boolean;
40 | }
41 |
42 | export type StackElement = Buffer | number;
43 | export type Stack = StackElement[];
44 | export type StackFunction = () => Stack;
45 |
46 | export { embed, p2ms, p2pk, p2pkh, p2sh, p2wpkh, p2wsh, p2tr };
47 |
48 | // TODO
49 | // witness commitment
50 |
--------------------------------------------------------------------------------
/ts_src/payments/lazy.ts:
--------------------------------------------------------------------------------
1 | export function prop(object: {}, name: string, f: () => any): void {
2 | Object.defineProperty(object, name, {
3 | configurable: true,
4 | enumerable: true,
5 | get(): any {
6 | const _value = f.call(this);
7 | this[name] = _value;
8 | return _value;
9 | },
10 | set(_value: any): void {
11 | Object.defineProperty(this, name, {
12 | configurable: true,
13 | enumerable: true,
14 | value: _value,
15 | writable: true,
16 | });
17 | },
18 | });
19 | }
20 |
21 | export function value(f: () => T): () => T {
22 | let _value: T;
23 | return (): T => {
24 | if (_value !== undefined) return _value;
25 | _value = f();
26 | return _value;
27 | };
28 | }
29 |
--------------------------------------------------------------------------------
/ts_src/payments/p2ms.ts:
--------------------------------------------------------------------------------
1 | import { bitcoin as BITCOIN_NETWORK } from '../networks';
2 | import * as bscript from '../script';
3 | import { isPoint, typeforce as typef } from '../types';
4 | import { Payment, PaymentOpts, Stack } from './index';
5 | import * as lazy from './lazy';
6 | const OPS = bscript.OPS;
7 |
8 | const OP_INT_BASE = OPS.OP_RESERVED; // OP_1 - 1
9 |
10 | function stacksEqual(a: Buffer[], b: Buffer[]): boolean {
11 | if (a.length !== b.length) return false;
12 |
13 | return a.every((x, i) => {
14 | return x.equals(b[i]);
15 | });
16 | }
17 |
18 | // input: OP_0 [signatures ...]
19 | // output: m [pubKeys ...] n OP_CHECKMULTISIG
20 | export function p2ms(a: Payment, opts?: PaymentOpts): Payment {
21 | if (
22 | !a.input &&
23 | !a.output &&
24 | !(a.pubkeys && a.m !== undefined) &&
25 | !a.signatures
26 | )
27 | throw new TypeError('Not enough data');
28 | opts = Object.assign({ validate: true }, opts || {});
29 |
30 | function isAcceptableSignature(x: Buffer | number): boolean {
31 | return (
32 | bscript.isCanonicalScriptSignature(x as Buffer) ||
33 | (opts!.allowIncomplete && (x as number) === OPS.OP_0) !== undefined
34 | );
35 | }
36 |
37 | typef(
38 | {
39 | network: typef.maybe(typef.Object),
40 | m: typef.maybe(typef.Number),
41 | n: typef.maybe(typef.Number),
42 | output: typef.maybe(typef.Buffer),
43 | pubkeys: typef.maybe(typef.arrayOf(isPoint)),
44 |
45 | signatures: typef.maybe(typef.arrayOf(isAcceptableSignature)),
46 | input: typef.maybe(typef.Buffer),
47 | },
48 | a,
49 | );
50 |
51 | const network = a.network || BITCOIN_NETWORK;
52 | const o: Payment = { network };
53 |
54 | let chunks: Stack = [];
55 | let decoded = false;
56 | function decode(output: Buffer | Stack): void {
57 | if (decoded) return;
58 | decoded = true;
59 | chunks = bscript.decompile(output) as Stack;
60 | o.m = (chunks[0] as number) - OP_INT_BASE;
61 | o.n = (chunks[chunks.length - 2] as number) - OP_INT_BASE;
62 | o.pubkeys = chunks.slice(1, -2) as Buffer[];
63 | }
64 |
65 | lazy.prop(o, 'output', () => {
66 | if (!a.m) return;
67 | if (!o.n) return;
68 | if (!a.pubkeys) return;
69 | return bscript.compile(
70 | ([] as Stack).concat(
71 | OP_INT_BASE + a.m,
72 | a.pubkeys,
73 | OP_INT_BASE + o.n,
74 | OPS.OP_CHECKMULTISIG,
75 | ),
76 | );
77 | });
78 | lazy.prop(o, 'm', () => {
79 | if (!o.output) return;
80 | decode(o.output);
81 | return o.m;
82 | });
83 | lazy.prop(o, 'n', () => {
84 | if (!o.pubkeys) return;
85 | return o.pubkeys.length;
86 | });
87 | lazy.prop(o, 'pubkeys', () => {
88 | if (!a.output) return;
89 | decode(a.output);
90 | return o.pubkeys;
91 | });
92 | lazy.prop(o, 'signatures', () => {
93 | if (!a.input) return;
94 | return bscript.decompile(a.input)!.slice(1);
95 | });
96 | lazy.prop(o, 'input', () => {
97 | if (!a.signatures) return;
98 | return bscript.compile(([OPS.OP_0] as Stack).concat(a.signatures));
99 | });
100 | lazy.prop(o, 'witness', () => {
101 | if (!o.input) return;
102 | return [];
103 | });
104 | lazy.prop(o, 'name', () => {
105 | if (!o.m || !o.n) return;
106 | return `p2ms(${o.m} of ${o.n})`;
107 | });
108 |
109 | // extended validation
110 | if (opts.validate) {
111 | if (a.output) {
112 | decode(a.output);
113 | if (!typef.Number(chunks[0])) throw new TypeError('Output is invalid');
114 | if (!typef.Number(chunks[chunks.length - 2]))
115 | throw new TypeError('Output is invalid');
116 | if (chunks[chunks.length - 1] !== OPS.OP_CHECKMULTISIG)
117 | throw new TypeError('Output is invalid');
118 |
119 | if (o.m! <= 0 || o.n! > 16 || o.m! > o.n! || o.n !== chunks.length - 3)
120 | throw new TypeError('Output is invalid');
121 | if (!o.pubkeys!.every(x => isPoint(x)))
122 | throw new TypeError('Output is invalid');
123 |
124 | if (a.m !== undefined && a.m !== o.m) throw new TypeError('m mismatch');
125 | if (a.n !== undefined && a.n !== o.n) throw new TypeError('n mismatch');
126 | if (a.pubkeys && !stacksEqual(a.pubkeys, o.pubkeys!))
127 | throw new TypeError('Pubkeys mismatch');
128 | }
129 |
130 | if (a.pubkeys) {
131 | if (a.n !== undefined && a.n !== a.pubkeys.length)
132 | throw new TypeError('Pubkey count mismatch');
133 | o.n = a.pubkeys.length;
134 |
135 | if (o.n < o.m!) throw new TypeError('Pubkey count cannot be less than m');
136 | }
137 |
138 | if (a.signatures) {
139 | if (a.signatures.length < o.m!)
140 | throw new TypeError('Not enough signatures provided');
141 | if (a.signatures.length > o.m!)
142 | throw new TypeError('Too many signatures provided');
143 | }
144 |
145 | if (a.input) {
146 | if (a.input[0] !== OPS.OP_0) throw new TypeError('Input is invalid');
147 | if (
148 | o.signatures!.length === 0 ||
149 | !o.signatures!.every(isAcceptableSignature)
150 | )
151 | throw new TypeError('Input has invalid signature(s)');
152 |
153 | if (a.signatures && !stacksEqual(a.signatures, o.signatures!))
154 | throw new TypeError('Signature mismatch');
155 | if (a.m !== undefined && a.m !== a.signatures!.length)
156 | throw new TypeError('Signature count mismatch');
157 | }
158 | }
159 |
160 | return Object.assign(o, a);
161 | }
162 |
--------------------------------------------------------------------------------
/ts_src/payments/p2pk.ts:
--------------------------------------------------------------------------------
1 | import { bitcoin as BITCOIN_NETWORK } from '../networks';
2 | import * as bscript from '../script';
3 | import { isPoint, typeforce as typef } from '../types';
4 | import { Payment, PaymentOpts, StackFunction } from './index';
5 | import * as lazy from './lazy';
6 | const OPS = bscript.OPS;
7 |
8 | // input: {signature}
9 | // output: {pubKey} OP_CHECKSIG
10 | export function p2pk(a: Payment, opts?: PaymentOpts): Payment {
11 | if (!a.input && !a.output && !a.pubkey && !a.input && !a.signature)
12 | throw new TypeError('Not enough data');
13 | opts = Object.assign({ validate: true }, opts || {});
14 |
15 | typef(
16 | {
17 | network: typef.maybe(typef.Object),
18 | output: typef.maybe(typef.Buffer),
19 | pubkey: typef.maybe(isPoint),
20 |
21 | signature: typef.maybe(bscript.isCanonicalScriptSignature),
22 | input: typef.maybe(typef.Buffer),
23 | },
24 | a,
25 | );
26 |
27 | const _chunks = lazy.value(() => {
28 | return bscript.decompile(a.input!);
29 | }) as StackFunction;
30 |
31 | const network = a.network || BITCOIN_NETWORK;
32 | const o: Payment = { name: 'p2pk', network };
33 |
34 | lazy.prop(o, 'output', () => {
35 | if (!a.pubkey) return;
36 | return bscript.compile([a.pubkey, OPS.OP_CHECKSIG]);
37 | });
38 | lazy.prop(o, 'pubkey', () => {
39 | if (!a.output) return;
40 | return a.output.slice(1, -1);
41 | });
42 | lazy.prop(o, 'signature', () => {
43 | if (!a.input) return;
44 | return _chunks()[0] as Buffer;
45 | });
46 | lazy.prop(o, 'input', () => {
47 | if (!a.signature) return;
48 | return bscript.compile([a.signature]);
49 | });
50 | lazy.prop(o, 'witness', () => {
51 | if (!o.input) return;
52 | return [];
53 | });
54 |
55 | // extended validation
56 | if (opts.validate) {
57 | if (a.output) {
58 | if (a.output[a.output.length - 1] !== OPS.OP_CHECKSIG)
59 | throw new TypeError('Output is invalid');
60 | if (!isPoint(o.pubkey)) throw new TypeError('Output pubkey is invalid');
61 | if (a.pubkey && !a.pubkey.equals(o.pubkey!))
62 | throw new TypeError('Pubkey mismatch');
63 | }
64 |
65 | if (a.signature) {
66 | if (a.input && !a.input.equals(o.input!))
67 | throw new TypeError('Signature mismatch');
68 | }
69 |
70 | if (a.input) {
71 | if (_chunks().length !== 1) throw new TypeError('Input is invalid');
72 | if (!bscript.isCanonicalScriptSignature(o.signature!))
73 | throw new TypeError('Input has invalid signature');
74 | }
75 | }
76 |
77 | return Object.assign(o, a);
78 | }
79 |
--------------------------------------------------------------------------------
/ts_src/payments/p2pkh.ts:
--------------------------------------------------------------------------------
1 | import * as bcrypto from '../crypto';
2 | import { bitcoin as BITCOIN_NETWORK } from '../networks';
3 | import * as bscript from '../script';
4 | import { isPoint, typeforce as typef } from '../types';
5 | import { Payment, PaymentOpts, StackFunction } from './index';
6 | import * as lazy from './lazy';
7 | import * as bs58check from 'bs58check';
8 | const OPS = bscript.OPS;
9 |
10 | // input: {signature} {pubkey}
11 | // output: OP_DUP OP_HASH160 {hash160(pubkey)} OP_EQUALVERIFY OP_CHECKSIG
12 | export function p2pkh(a: Payment, opts?: PaymentOpts): Payment {
13 | if (!a.address && !a.hash && !a.output && !a.pubkey && !a.input)
14 | throw new TypeError('Not enough data');
15 | opts = Object.assign({ validate: true }, opts || {});
16 |
17 | typef(
18 | {
19 | network: typef.maybe(typef.Object),
20 | address: typef.maybe(typef.String),
21 | hash: typef.maybe(typef.BufferN(20)),
22 | output: typef.maybe(typef.BufferN(25)),
23 |
24 | pubkey: typef.maybe(isPoint),
25 | signature: typef.maybe(bscript.isCanonicalScriptSignature),
26 | input: typef.maybe(typef.Buffer),
27 | },
28 | a,
29 | );
30 |
31 | const _address = lazy.value(() => {
32 | const payload = Buffer.from(bs58check.decode(a.address!));
33 | const version = payload.readUInt8(0);
34 | const hash = payload.slice(1);
35 | return { version, hash };
36 | });
37 | const _chunks = lazy.value(() => {
38 | return bscript.decompile(a.input!);
39 | }) as StackFunction;
40 |
41 | const network = a.network || BITCOIN_NETWORK;
42 | const o: Payment = { name: 'p2pkh', network };
43 |
44 | lazy.prop(o, 'address', () => {
45 | if (!o.hash) return;
46 |
47 | const payload = Buffer.allocUnsafe(21);
48 | payload.writeUInt8(network.pubKeyHash, 0);
49 | o.hash.copy(payload, 1);
50 | return bs58check.encode(payload);
51 | });
52 | lazy.prop(o, 'hash', () => {
53 | if (a.output) return a.output.slice(3, 23);
54 | if (a.address) return _address().hash;
55 | if (a.pubkey || o.pubkey) return bcrypto.hash160(a.pubkey! || o.pubkey!);
56 | });
57 | lazy.prop(o, 'output', () => {
58 | if (!o.hash) return;
59 | return bscript.compile([
60 | OPS.OP_DUP,
61 | OPS.OP_HASH160,
62 | o.hash,
63 | OPS.OP_EQUALVERIFY,
64 | OPS.OP_CHECKSIG,
65 | ]);
66 | });
67 | lazy.prop(o, 'pubkey', () => {
68 | if (!a.input) return;
69 | return _chunks()[1] as Buffer;
70 | });
71 | lazy.prop(o, 'signature', () => {
72 | if (!a.input) return;
73 | return _chunks()[0] as Buffer;
74 | });
75 | lazy.prop(o, 'input', () => {
76 | if (!a.pubkey) return;
77 | if (!a.signature) return;
78 | return bscript.compile([a.signature, a.pubkey]);
79 | });
80 | lazy.prop(o, 'witness', () => {
81 | if (!o.input) return;
82 | return [];
83 | });
84 |
85 | // extended validation
86 | if (opts.validate) {
87 | let hash: Buffer = Buffer.from([]);
88 | if (a.address) {
89 | if (_address().version !== network.pubKeyHash)
90 | throw new TypeError('Invalid version or Network mismatch');
91 | if (_address().hash.length !== 20) throw new TypeError('Invalid address');
92 | hash = _address().hash;
93 | }
94 |
95 | if (a.hash) {
96 | if (hash.length > 0 && !hash.equals(a.hash))
97 | throw new TypeError('Hash mismatch');
98 | else hash = a.hash;
99 | }
100 |
101 | if (a.output) {
102 | if (
103 | a.output.length !== 25 ||
104 | a.output[0] !== OPS.OP_DUP ||
105 | a.output[1] !== OPS.OP_HASH160 ||
106 | a.output[2] !== 0x14 ||
107 | a.output[23] !== OPS.OP_EQUALVERIFY ||
108 | a.output[24] !== OPS.OP_CHECKSIG
109 | )
110 | throw new TypeError('Output is invalid');
111 |
112 | const hash2 = a.output.slice(3, 23);
113 | if (hash.length > 0 && !hash.equals(hash2))
114 | throw new TypeError('Hash mismatch');
115 | else hash = hash2;
116 | }
117 |
118 | if (a.pubkey) {
119 | const pkh = bcrypto.hash160(a.pubkey);
120 | if (hash.length > 0 && !hash.equals(pkh))
121 | throw new TypeError('Hash mismatch');
122 | else hash = pkh;
123 | }
124 |
125 | if (a.input) {
126 | const chunks = _chunks();
127 | if (chunks.length !== 2) throw new TypeError('Input is invalid');
128 | if (!bscript.isCanonicalScriptSignature(chunks[0] as Buffer))
129 | throw new TypeError('Input has invalid signature');
130 | if (!isPoint(chunks[1])) throw new TypeError('Input has invalid pubkey');
131 |
132 | if (a.signature && !a.signature.equals(chunks[0] as Buffer))
133 | throw new TypeError('Signature mismatch');
134 | if (a.pubkey && !a.pubkey.equals(chunks[1] as Buffer))
135 | throw new TypeError('Pubkey mismatch');
136 |
137 | const pkh = bcrypto.hash160(chunks[1] as Buffer);
138 | if (hash.length > 0 && !hash.equals(pkh))
139 | throw new TypeError('Hash mismatch');
140 | }
141 | }
142 |
143 | return Object.assign(o, a);
144 | }
145 |
--------------------------------------------------------------------------------
/ts_src/payments/p2wpkh.ts:
--------------------------------------------------------------------------------
1 | import * as bcrypto from '../crypto';
2 | import { bitcoin as BITCOIN_NETWORK } from '../networks';
3 | import * as bscript from '../script';
4 | import { isPoint, typeforce as typef } from '../types';
5 | import { Payment, PaymentOpts } from './index';
6 | import * as lazy from './lazy';
7 | import { bech32 } from 'bech32';
8 | const OPS = bscript.OPS;
9 |
10 | const EMPTY_BUFFER = Buffer.alloc(0);
11 |
12 | // witness: {signature} {pubKey}
13 | // input: <>
14 | // output: OP_0 {pubKeyHash}
15 | export function p2wpkh(a: Payment, opts?: PaymentOpts): Payment {
16 | if (!a.address && !a.hash && !a.output && !a.pubkey && !a.witness)
17 | throw new TypeError('Not enough data');
18 | opts = Object.assign({ validate: true }, opts || {});
19 |
20 | typef(
21 | {
22 | address: typef.maybe(typef.String),
23 | hash: typef.maybe(typef.BufferN(20)),
24 | input: typef.maybe(typef.BufferN(0)),
25 | network: typef.maybe(typef.Object),
26 | output: typef.maybe(typef.BufferN(22)),
27 | pubkey: typef.maybe(isPoint),
28 | signature: typef.maybe(bscript.isCanonicalScriptSignature),
29 | witness: typef.maybe(typef.arrayOf(typef.Buffer)),
30 | },
31 | a,
32 | );
33 |
34 | const _address = lazy.value(() => {
35 | const result = bech32.decode(a.address!);
36 | const version = result.words.shift();
37 | const data = bech32.fromWords(result.words);
38 | return {
39 | version,
40 | prefix: result.prefix,
41 | data: Buffer.from(data),
42 | };
43 | });
44 |
45 | const network = a.network || BITCOIN_NETWORK;
46 | const o: Payment = { name: 'p2wpkh', network };
47 |
48 | lazy.prop(o, 'address', () => {
49 | if (!o.hash) return;
50 |
51 | const words = bech32.toWords(o.hash);
52 | words.unshift(0x00);
53 | return bech32.encode(network.bech32, words);
54 | });
55 | lazy.prop(o, 'hash', () => {
56 | if (a.output) return a.output.slice(2, 22);
57 | if (a.address) return _address().data;
58 | if (a.pubkey || o.pubkey) return bcrypto.hash160(a.pubkey! || o.pubkey!);
59 | });
60 | lazy.prop(o, 'output', () => {
61 | if (!o.hash) return;
62 | return bscript.compile([OPS.OP_0, o.hash]);
63 | });
64 | lazy.prop(o, 'pubkey', () => {
65 | if (a.pubkey) return a.pubkey;
66 | if (!a.witness) return;
67 | return a.witness[1];
68 | });
69 | lazy.prop(o, 'signature', () => {
70 | if (!a.witness) return;
71 | return a.witness[0];
72 | });
73 | lazy.prop(o, 'input', () => {
74 | if (!o.witness) return;
75 | return EMPTY_BUFFER;
76 | });
77 | lazy.prop(o, 'witness', () => {
78 | if (!a.pubkey) return;
79 | if (!a.signature) return;
80 | return [a.signature, a.pubkey];
81 | });
82 |
83 | // extended validation
84 | if (opts.validate) {
85 | let hash: Buffer = Buffer.from([]);
86 | if (a.address) {
87 | if (network && network.bech32 !== _address().prefix)
88 | throw new TypeError('Invalid prefix or Network mismatch');
89 | if (_address().version !== 0x00)
90 | throw new TypeError('Invalid address version');
91 | if (_address().data.length !== 20)
92 | throw new TypeError('Invalid address data');
93 | hash = _address().data;
94 | }
95 |
96 | if (a.hash) {
97 | if (hash.length > 0 && !hash.equals(a.hash))
98 | throw new TypeError('Hash mismatch');
99 | else hash = a.hash;
100 | }
101 |
102 | if (a.output) {
103 | if (
104 | a.output.length !== 22 ||
105 | a.output[0] !== OPS.OP_0 ||
106 | a.output[1] !== 0x14
107 | )
108 | throw new TypeError('Output is invalid');
109 | if (hash.length > 0 && !hash.equals(a.output.slice(2)))
110 | throw new TypeError('Hash mismatch');
111 | else hash = a.output.slice(2);
112 | }
113 |
114 | if (a.pubkey) {
115 | const pkh = bcrypto.hash160(a.pubkey);
116 | if (hash.length > 0 && !hash.equals(pkh))
117 | throw new TypeError('Hash mismatch');
118 | else hash = pkh;
119 | if (!isPoint(a.pubkey) || a.pubkey.length !== 33)
120 | throw new TypeError('Invalid pubkey for p2wpkh');
121 | }
122 |
123 | if (a.witness) {
124 | if (a.witness.length !== 2) throw new TypeError('Witness is invalid');
125 | if (!bscript.isCanonicalScriptSignature(a.witness[0]))
126 | throw new TypeError('Witness has invalid signature');
127 | if (!isPoint(a.witness[1]) || a.witness[1].length !== 33)
128 | throw new TypeError('Witness has invalid pubkey');
129 |
130 | if (a.signature && !a.signature.equals(a.witness[0]))
131 | throw new TypeError('Signature mismatch');
132 | if (a.pubkey && !a.pubkey.equals(a.witness[1]))
133 | throw new TypeError('Pubkey mismatch');
134 |
135 | const pkh = bcrypto.hash160(a.witness[1]);
136 | if (hash.length > 0 && !hash.equals(pkh))
137 | throw new TypeError('Hash mismatch');
138 | }
139 | }
140 |
141 | return Object.assign(o, a);
142 | }
143 |
--------------------------------------------------------------------------------
/ts_src/psbt/psbtutils.ts:
--------------------------------------------------------------------------------
1 | import * as varuint from 'bip174/src/lib/converter/varint';
2 | import { PartialSig, PsbtInput } from 'bip174/src/lib/interfaces';
3 | import * as bscript from '../script';
4 | import { Transaction } from '../transaction';
5 | import { hash160 } from '../crypto';
6 | import * as payments from '../payments';
7 |
8 | function isPaymentFactory(payment: any): (script: Buffer) => boolean {
9 | return (script: Buffer): boolean => {
10 | try {
11 | payment({ output: script });
12 | return true;
13 | } catch (err) {
14 | return false;
15 | }
16 | };
17 | }
18 | export const isP2MS = isPaymentFactory(payments.p2ms);
19 | export const isP2PK = isPaymentFactory(payments.p2pk);
20 | export const isP2PKH = isPaymentFactory(payments.p2pkh);
21 | export const isP2WPKH = isPaymentFactory(payments.p2wpkh);
22 | export const isP2WSHScript = isPaymentFactory(payments.p2wsh);
23 | export const isP2SHScript = isPaymentFactory(payments.p2sh);
24 | export const isP2TR = isPaymentFactory(payments.p2tr);
25 |
26 | export function witnessStackToScriptWitness(witness: Buffer[]): Buffer {
27 | let buffer = Buffer.allocUnsafe(0);
28 |
29 | function writeSlice(slice: Buffer): void {
30 | buffer = Buffer.concat([buffer, Buffer.from(slice)]);
31 | }
32 |
33 | function writeVarInt(i: number): void {
34 | const currentLen = buffer.length;
35 | const varintLen = varuint.encodingLength(i);
36 |
37 | buffer = Buffer.concat([buffer, Buffer.allocUnsafe(varintLen)]);
38 | varuint.encode(i, buffer, currentLen);
39 | }
40 |
41 | function writeVarSlice(slice: Buffer): void {
42 | writeVarInt(slice.length);
43 | writeSlice(slice);
44 | }
45 |
46 | function writeVector(vector: Buffer[]): void {
47 | writeVarInt(vector.length);
48 | vector.forEach(writeVarSlice);
49 | }
50 |
51 | writeVector(witness);
52 |
53 | return buffer;
54 | }
55 |
56 | export function pubkeyPositionInScript(pubkey: Buffer, script: Buffer): number {
57 | const pubkeyHash = hash160(pubkey);
58 | const pubkeyXOnly = pubkey.slice(1, 33); // slice before calling?
59 |
60 | const decompiled = bscript.decompile(script);
61 | if (decompiled === null) throw new Error('Unknown script error');
62 |
63 | return decompiled.findIndex(element => {
64 | if (typeof element === 'number') return false;
65 | return (
66 | element.equals(pubkey) ||
67 | element.equals(pubkeyHash) ||
68 | element.equals(pubkeyXOnly)
69 | );
70 | });
71 | }
72 |
73 | export function pubkeyInScript(pubkey: Buffer, script: Buffer): boolean {
74 | return pubkeyPositionInScript(pubkey, script) !== -1;
75 | }
76 |
77 | export function checkInputForSig(input: PsbtInput, action: string): boolean {
78 | const pSigs = extractPartialSigs(input);
79 | return pSigs.some(pSig =>
80 | signatureBlocksAction(pSig, bscript.signature.decode, action),
81 | );
82 | }
83 |
84 | type SignatureDecodeFunc = (buffer: Buffer) => {
85 | signature: Buffer;
86 | hashType: number;
87 | };
88 | export function signatureBlocksAction(
89 | signature: Buffer,
90 | signatureDecodeFn: SignatureDecodeFunc,
91 | action: string,
92 | ): boolean {
93 | const { hashType } = signatureDecodeFn(signature);
94 | const whitelist: string[] = [];
95 | const isAnyoneCanPay = hashType & Transaction.SIGHASH_ANYONECANPAY;
96 | if (isAnyoneCanPay) whitelist.push('addInput');
97 | const hashMod = hashType & 0x1f;
98 | switch (hashMod) {
99 | case Transaction.SIGHASH_ALL:
100 | break;
101 | case Transaction.SIGHASH_SINGLE:
102 | case Transaction.SIGHASH_NONE:
103 | whitelist.push('addOutput');
104 | whitelist.push('setInputSequence');
105 | break;
106 | }
107 | if (whitelist.indexOf(action) === -1) {
108 | return true;
109 | }
110 | return false;
111 | }
112 |
113 | function extractPartialSigs(input: PsbtInput): Buffer[] {
114 | let pSigs: PartialSig[] = [];
115 | if ((input.partialSig || []).length === 0) {
116 | if (!input.finalScriptSig && !input.finalScriptWitness) return [];
117 | pSigs = getPsigsFromInputFinalScripts(input);
118 | } else {
119 | pSigs = input.partialSig!;
120 | }
121 | return pSigs.map(p => p.signature);
122 | }
123 |
124 | function getPsigsFromInputFinalScripts(input: PsbtInput): PartialSig[] {
125 | const scriptItems = !input.finalScriptSig
126 | ? []
127 | : bscript.decompile(input.finalScriptSig) || [];
128 | const witnessItems = !input.finalScriptWitness
129 | ? []
130 | : bscript.decompile(input.finalScriptWitness) || [];
131 | return scriptItems
132 | .concat(witnessItems)
133 | .filter(item => {
134 | return Buffer.isBuffer(item) && bscript.isCanonicalScriptSignature(item);
135 | })
136 | .map(sig => ({ signature: sig })) as PartialSig[];
137 | }
138 |
--------------------------------------------------------------------------------
/ts_src/push_data.ts:
--------------------------------------------------------------------------------
1 | import { OPS } from './ops';
2 |
3 | export function encodingLength(i: number): number {
4 | return i < OPS.OP_PUSHDATA1 ? 1 : i <= 0xff ? 2 : i <= 0xffff ? 3 : 5;
5 | }
6 |
7 | export function encode(buffer: Buffer, num: number, offset: number): number {
8 | const size = encodingLength(num);
9 |
10 | // ~6 bit
11 | if (size === 1) {
12 | buffer.writeUInt8(num, offset);
13 |
14 | // 8 bit
15 | } else if (size === 2) {
16 | buffer.writeUInt8(OPS.OP_PUSHDATA1, offset);
17 | buffer.writeUInt8(num, offset + 1);
18 |
19 | // 16 bit
20 | } else if (size === 3) {
21 | buffer.writeUInt8(OPS.OP_PUSHDATA2, offset);
22 | buffer.writeUInt16LE(num, offset + 1);
23 |
24 | // 32 bit
25 | } else {
26 | buffer.writeUInt8(OPS.OP_PUSHDATA4, offset);
27 | buffer.writeUInt32LE(num, offset + 1);
28 | }
29 |
30 | return size;
31 | }
32 |
33 | export function decode(
34 | buffer: Buffer,
35 | offset: number,
36 | ): {
37 | opcode: number;
38 | number: number;
39 | size: number;
40 | } | null {
41 | const opcode = buffer.readUInt8(offset);
42 | let num: number;
43 | let size: number;
44 |
45 | // ~6 bit
46 | if (opcode < OPS.OP_PUSHDATA1) {
47 | num = opcode;
48 | size = 1;
49 |
50 | // 8 bit
51 | } else if (opcode === OPS.OP_PUSHDATA1) {
52 | if (offset + 2 > buffer.length) return null;
53 | num = buffer.readUInt8(offset + 1);
54 | size = 2;
55 |
56 | // 16 bit
57 | } else if (opcode === OPS.OP_PUSHDATA2) {
58 | if (offset + 3 > buffer.length) return null;
59 | num = buffer.readUInt16LE(offset + 1);
60 | size = 3;
61 |
62 | // 32 bit
63 | } else {
64 | if (offset + 5 > buffer.length) return null;
65 | if (opcode !== OPS.OP_PUSHDATA4) throw new Error('Unexpected opcode');
66 |
67 | num = buffer.readUInt32LE(offset + 1);
68 | size = 5;
69 | }
70 |
71 | return {
72 | opcode,
73 | number: num,
74 | size,
75 | };
76 | }
77 |
--------------------------------------------------------------------------------
/ts_src/script_number.ts:
--------------------------------------------------------------------------------
1 | export function decode(
2 | buffer: Buffer,
3 | maxLength?: number,
4 | minimal?: boolean,
5 | ): number {
6 | maxLength = maxLength || 4;
7 | minimal = minimal === undefined ? true : minimal;
8 |
9 | const length = buffer.length;
10 | if (length === 0) return 0;
11 | if (length > maxLength) throw new TypeError('Script number overflow');
12 | if (minimal) {
13 | if ((buffer[length - 1] & 0x7f) === 0) {
14 | if (length <= 1 || (buffer[length - 2] & 0x80) === 0)
15 | throw new Error('Non-minimally encoded script number');
16 | }
17 | }
18 |
19 | // 40-bit
20 | if (length === 5) {
21 | const a = buffer.readUInt32LE(0);
22 | const b = buffer.readUInt8(4);
23 |
24 | if (b & 0x80) return -((b & ~0x80) * 0x100000000 + a);
25 | return b * 0x100000000 + a;
26 | }
27 |
28 | // 32-bit / 24-bit / 16-bit / 8-bit
29 | let result = 0;
30 | for (let i = 0; i < length; ++i) {
31 | result |= buffer[i] << (8 * i);
32 | }
33 |
34 | if (buffer[length - 1] & 0x80)
35 | return -(result & ~(0x80 << (8 * (length - 1))));
36 | return result;
37 | }
38 |
39 | function scriptNumSize(i: number): number {
40 | return i > 0x7fffffff
41 | ? 5
42 | : i > 0x7fffff
43 | ? 4
44 | : i > 0x7fff
45 | ? 3
46 | : i > 0x7f
47 | ? 2
48 | : i > 0x00
49 | ? 1
50 | : 0;
51 | }
52 |
53 | export function encode(_number: number): Buffer {
54 | let value = Math.abs(_number);
55 | const size = scriptNumSize(value);
56 | const buffer = Buffer.allocUnsafe(size);
57 | const negative = _number < 0;
58 |
59 | for (let i = 0; i < size; ++i) {
60 | buffer.writeUInt8(value & 0xff, i);
61 | value >>= 8;
62 | }
63 |
64 | if (buffer[size - 1] & 0x80) {
65 | buffer.writeUInt8(negative ? 0x80 : 0x00, size - 1);
66 | } else if (negative) {
67 | buffer[size - 1] |= 0x80;
68 | }
69 |
70 | return buffer;
71 | }
72 |
--------------------------------------------------------------------------------
/ts_src/script_signature.ts:
--------------------------------------------------------------------------------
1 | import * as bip66 from './bip66';
2 | import * as types from './types';
3 | const { typeforce } = types;
4 |
5 | const ZERO = Buffer.alloc(1, 0);
6 | function toDER(x: Buffer): Buffer {
7 | let i = 0;
8 | while (x[i] === 0) ++i;
9 | if (i === x.length) return ZERO;
10 | x = x.slice(i);
11 | if (x[0] & 0x80) return Buffer.concat([ZERO, x], 1 + x.length);
12 | return x;
13 | }
14 |
15 | function fromDER(x: Buffer): Buffer {
16 | if (x[0] === 0x00) x = x.slice(1);
17 | const buffer = Buffer.alloc(32, 0);
18 | const bstart = Math.max(0, 32 - x.length);
19 | x.copy(buffer, bstart);
20 | return buffer;
21 | }
22 |
23 | interface ScriptSignature {
24 | signature: Buffer;
25 | hashType: number;
26 | }
27 |
28 | // BIP62: 1 byte hashType flag (only 0x01, 0x02, 0x03, 0x81, 0x82 and 0x83 are allowed)
29 | export function decode(buffer: Buffer): ScriptSignature {
30 | const hashType = buffer.readUInt8(buffer.length - 1);
31 | const hashTypeMod = hashType & ~0x80;
32 | if (hashTypeMod <= 0 || hashTypeMod >= 4)
33 | throw new Error('Invalid hashType ' + hashType);
34 |
35 | const decoded = bip66.decode(buffer.slice(0, -1));
36 | const r = fromDER(decoded.r);
37 | const s = fromDER(decoded.s);
38 | const signature = Buffer.concat([r, s], 64);
39 |
40 | return { signature, hashType };
41 | }
42 |
43 | export function encode(signature: Buffer, hashType: number): Buffer {
44 | typeforce(
45 | {
46 | signature: types.BufferN(64),
47 | hashType: types.UInt8,
48 | },
49 | { signature, hashType },
50 | );
51 |
52 | const hashTypeMod = hashType & ~0x80;
53 | if (hashTypeMod <= 0 || hashTypeMod >= 4)
54 | throw new Error('Invalid hashType ' + hashType);
55 |
56 | const hashTypeBuffer = Buffer.allocUnsafe(1);
57 | hashTypeBuffer.writeUInt8(hashType, 0);
58 |
59 | const r = toDER(signature.slice(0, 32));
60 | const s = toDER(signature.slice(32, 64));
61 |
62 | return Buffer.concat([bip66.encode(r, s), hashTypeBuffer]);
63 | }
64 |
--------------------------------------------------------------------------------
/ts_src/types.ts:
--------------------------------------------------------------------------------
1 | import { Buffer as NBuffer } from 'buffer';
2 |
3 | export const typeforce = require('typeforce');
4 |
5 | const ZERO32 = NBuffer.alloc(32, 0);
6 | const EC_P = NBuffer.from(
7 | 'fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f',
8 | 'hex',
9 | );
10 |
11 | export function isPoint(p: Buffer | number | undefined | null): boolean {
12 | if (!NBuffer.isBuffer(p)) return false;
13 | if (p.length < 33) return false;
14 |
15 | const t = p[0];
16 | const x = p.slice(1, 33);
17 | if (x.compare(ZERO32) === 0) return false;
18 | if (x.compare(EC_P) >= 0) return false;
19 | if ((t === 0x02 || t === 0x03) && p.length === 33) {
20 | return true;
21 | }
22 |
23 | const y = p.slice(33);
24 | if (y.compare(ZERO32) === 0) return false;
25 | if (y.compare(EC_P) >= 0) return false;
26 | if (t === 0x04 && p.length === 65) return true;
27 | return false;
28 | }
29 |
30 | const UINT31_MAX: number = Math.pow(2, 31) - 1;
31 | export function UInt31(value: number): boolean {
32 | return typeforce.UInt32(value) && value <= UINT31_MAX;
33 | }
34 |
35 | export function BIP32Path(value: string): boolean {
36 | return typeforce.String(value) && !!value.match(/^(m\/)?(\d+'?\/)*\d+'?$/);
37 | }
38 | BIP32Path.toJSON = (): string => {
39 | return 'BIP32 derivation path';
40 | };
41 |
42 | export function Signer(obj: any): boolean {
43 | return (
44 | (typeforce.Buffer(obj.publicKey) ||
45 | typeof obj.getPublicKey === 'function') &&
46 | typeof obj.sign === 'function'
47 | );
48 | }
49 |
50 | const SATOSHI_MAX: number = 21 * 1e14;
51 | export function Satoshi(value: number): boolean {
52 | return typeforce.UInt53(value) && value <= SATOSHI_MAX;
53 | }
54 |
55 | // external dependent types
56 | export const ECPoint = typeforce.quacksLike('Point');
57 |
58 | // exposed, external API
59 | export const Network = typeforce.compile({
60 | messagePrefix: typeforce.oneOf(typeforce.Buffer, typeforce.String),
61 | bip32: {
62 | public: typeforce.UInt32,
63 | private: typeforce.UInt32,
64 | },
65 | pubKeyHash: typeforce.UInt8,
66 | scriptHash: typeforce.UInt8,
67 | wif: typeforce.UInt8,
68 | });
69 |
70 | export interface XOnlyPointAddTweakResult {
71 | parity: 1 | 0;
72 | xOnlyPubkey: Uint8Array;
73 | }
74 |
75 | export interface Tapleaf {
76 | output: Buffer;
77 | version?: number;
78 | }
79 |
80 | export const TAPLEAF_VERSION_MASK = 0xfe;
81 | export function isTapleaf(o: any): o is Tapleaf {
82 | if (!o || !('output' in o)) return false;
83 | if (!NBuffer.isBuffer(o.output)) return false;
84 | if (o.version !== undefined)
85 | return (o.version & TAPLEAF_VERSION_MASK) === o.version;
86 | return true;
87 | }
88 |
89 | /**
90 | * Binary tree repsenting script path spends for a Taproot input.
91 | * Each node is either a single Tapleaf, or a pair of Tapleaf | Taptree.
92 | * The tree has no balancing requirements.
93 | */
94 | export type Taptree = [Taptree | Tapleaf, Taptree | Tapleaf] | Tapleaf;
95 |
96 | export function isTaptree(scriptTree: any): scriptTree is Taptree {
97 | if (!Array(scriptTree)) return isTapleaf(scriptTree);
98 | if (scriptTree.length !== 2) return false;
99 | return scriptTree.every((t: any) => isTaptree(t));
100 | }
101 |
102 | export interface TinySecp256k1Interface {
103 | isXOnlyPoint(p: Uint8Array): boolean;
104 | xOnlyPointAddTweak(
105 | p: Uint8Array,
106 | tweak: Uint8Array,
107 | ): XOnlyPointAddTweakResult | null;
108 | }
109 |
110 | export const Buffer256bit = typeforce.BufferN(32);
111 | export const Hash160bit = typeforce.BufferN(20);
112 | export const Hash256bit = typeforce.BufferN(32);
113 | export const Number = typeforce.Number;
114 | export const Array = typeforce.Array;
115 | export const Boolean = typeforce.Boolean;
116 | export const String = typeforce.String;
117 | export const Buffer = typeforce.Buffer;
118 | export const Hex = typeforce.Hex;
119 | export const maybe = typeforce.maybe;
120 | export const tuple = typeforce.tuple;
121 | export const UInt8 = typeforce.UInt8;
122 | export const UInt32 = typeforce.UInt32;
123 | export const Function = typeforce.Function;
124 | export const BufferN = typeforce.BufferN;
125 | export const Null = typeforce.Null;
126 | export const oneOf = typeforce.oneOf;
127 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2020",
4 | "module": "commonjs",
5 | "outDir": "./src",
6 | "declaration": true,
7 | "rootDir": "./ts_src",
8 | "types": [
9 | "node"
10 | ],
11 | "allowJs": false,
12 | "strict": true,
13 | "esModuleInterop": false,
14 | "noUnusedLocals": true,
15 | "noUnusedParameters": true
16 | },
17 | "include": [
18 | "ts_src/**/*.ts"
19 | ],
20 | "exclude": [
21 | "**/*.spec.ts",
22 | "node_modules/**/*"
23 | ]
24 | }
25 |
--------------------------------------------------------------------------------