├── .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 | --------------------------------------------------------------------------------