@bitcoinerlab/descriptors-core 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +710 -0
- package/dist/adapters/applyPR2137.d.ts +2 -0
- package/dist/adapters/applyPR2137.js +150 -0
- package/dist/adapters/bitcoinjs.d.ts +8 -0
- package/dist/adapters/bitcoinjs.js +36 -0
- package/dist/adapters/scure/address.d.ts +2 -0
- package/dist/adapters/scure/address.js +50 -0
- package/dist/adapters/scure/bip32.d.ts +2 -0
- package/dist/adapters/scure/bip32.js +16 -0
- package/dist/adapters/scure/common.d.ts +14 -0
- package/dist/adapters/scure/common.js +36 -0
- package/dist/adapters/scure/ecpair.d.ts +2 -0
- package/dist/adapters/scure/ecpair.js +58 -0
- package/dist/adapters/scure/payments.d.ts +2 -0
- package/dist/adapters/scure/payments.js +216 -0
- package/dist/adapters/scure/psbt.d.ts +43 -0
- package/dist/adapters/scure/psbt.js +382 -0
- package/dist/adapters/scure/script.d.ts +20 -0
- package/dist/adapters/scure/script.js +163 -0
- package/dist/adapters/scure/transaction.d.ts +2 -0
- package/dist/adapters/scure/transaction.js +32 -0
- package/dist/adapters/scure.d.ts +6 -0
- package/dist/adapters/scure.js +37 -0
- package/dist/adapters/scureKeys.d.ts +4 -0
- package/dist/adapters/scureKeys.js +135 -0
- package/dist/bip174.d.ts +87 -0
- package/dist/bip174.js +12 -0
- package/dist/bitcoinLib.d.ts +385 -0
- package/dist/bitcoinLib.js +19 -0
- package/dist/bitcoinjs-lib-internals.d.ts +6 -0
- package/dist/bitcoinjs-lib-internals.js +60 -0
- package/dist/bitcoinjs.d.ts +12 -0
- package/dist/bitcoinjs.js +18 -0
- package/dist/checksum.d.ts +6 -0
- package/dist/checksum.js +58 -0
- package/dist/crypto.d.ts +3 -0
- package/dist/crypto.js +79 -0
- package/dist/descriptors.d.ts +481 -0
- package/dist/descriptors.js +1888 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.js +87 -0
- package/dist/keyExpressions.d.ts +124 -0
- package/dist/keyExpressions.js +310 -0
- package/dist/keyInterfaces.d.ts +5 -0
- package/dist/keyInterfaces.js +50 -0
- package/dist/ledger.d.ts +183 -0
- package/dist/ledger.js +618 -0
- package/dist/miniscript.d.ts +125 -0
- package/dist/miniscript.js +310 -0
- package/dist/multipath.d.ts +13 -0
- package/dist/multipath.js +76 -0
- package/dist/networkUtils.d.ts +3 -0
- package/dist/networkUtils.js +16 -0
- package/dist/networks.d.ts +16 -0
- package/dist/networks.js +31 -0
- package/dist/parseUtils.d.ts +7 -0
- package/dist/parseUtils.js +46 -0
- package/dist/psbt.d.ts +40 -0
- package/dist/psbt.js +228 -0
- package/dist/re.d.ts +31 -0
- package/dist/re.js +79 -0
- package/dist/resourceLimits.d.ts +28 -0
- package/dist/resourceLimits.js +84 -0
- package/dist/scriptExpressions.d.ts +95 -0
- package/dist/scriptExpressions.js +98 -0
- package/dist/scure.d.ts +4 -0
- package/dist/scure.js +10 -0
- package/dist/signers.d.ts +161 -0
- package/dist/signers.js +324 -0
- package/dist/tapMiniscript.d.ts +231 -0
- package/dist/tapMiniscript.js +524 -0
- package/dist/tapTree.d.ts +91 -0
- package/dist/tapTree.js +166 -0
- package/dist/types.d.ts +296 -0
- package/dist/types.js +4 -0
- package/package.json +148 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export type { Expansion, ExpansionMap, KeyExpressionParser, KeyInfo, Preimage, TimeConstraints } from './types';
|
|
2
|
+
export type { TreeNode, TapTreeNode, TapTreeInfoNode, TapLeaf, TapLeafInfo } from './tapTree';
|
|
3
|
+
export { networks, type Network } from './networks';
|
|
4
|
+
export { DescriptorsFactory, OutputInstance, OutputConstructor } from './descriptors';
|
|
5
|
+
export { DescriptorChecksum as checksum } from './checksum';
|
|
6
|
+
import * as signers from './signers';
|
|
7
|
+
export { signers };
|
|
8
|
+
export { keyExpressionBIP32, keyExpressionLedger } from './keyExpressions';
|
|
9
|
+
import * as scriptExpressions from './scriptExpressions';
|
|
10
|
+
export { scriptExpressions };
|
|
11
|
+
import { LedgerState, getLedgerMasterFingerPrint, getLedgerXpub, registerLedgerWallet, assertLedgerApp, LedgerManager } from './ledger';
|
|
12
|
+
/** @namespace */
|
|
13
|
+
export declare const ledger: {
|
|
14
|
+
/** @function */
|
|
15
|
+
getLedgerMasterFingerPrint: typeof getLedgerMasterFingerPrint;
|
|
16
|
+
/** @function */
|
|
17
|
+
getLedgerXpub: typeof getLedgerXpub;
|
|
18
|
+
/** @function */
|
|
19
|
+
registerLedgerWallet: typeof registerLedgerWallet;
|
|
20
|
+
/** @function */
|
|
21
|
+
assertLedgerApp: typeof assertLedgerApp;
|
|
22
|
+
};
|
|
23
|
+
export type { LedgerState, LedgerManager };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright (c) 2023 Jose-Luis Landabaso - https://bitcoinerlab.com
|
|
3
|
+
// Distributed under the MIT software license
|
|
4
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
5
|
+
if (k2 === undefined) k2 = k;
|
|
6
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
7
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
8
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
9
|
+
}
|
|
10
|
+
Object.defineProperty(o, k2, desc);
|
|
11
|
+
}) : (function(o, m, k, k2) {
|
|
12
|
+
if (k2 === undefined) k2 = k;
|
|
13
|
+
o[k2] = m[k];
|
|
14
|
+
}));
|
|
15
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
16
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
17
|
+
}) : function(o, v) {
|
|
18
|
+
o["default"] = v;
|
|
19
|
+
});
|
|
20
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
21
|
+
var ownKeys = function(o) {
|
|
22
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
23
|
+
var ar = [];
|
|
24
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
25
|
+
return ar;
|
|
26
|
+
};
|
|
27
|
+
return ownKeys(o);
|
|
28
|
+
};
|
|
29
|
+
return function (mod) {
|
|
30
|
+
if (mod && mod.__esModule) return mod;
|
|
31
|
+
var result = {};
|
|
32
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
33
|
+
__setModuleDefault(result, mod);
|
|
34
|
+
return result;
|
|
35
|
+
};
|
|
36
|
+
})();
|
|
37
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
38
|
+
exports.ledger = exports.scriptExpressions = exports.keyExpressionLedger = exports.keyExpressionBIP32 = exports.signers = exports.checksum = exports.DescriptorsFactory = exports.networks = void 0;
|
|
39
|
+
// Some dependencies (like hash-base) assume process.version exists.
|
|
40
|
+
// In React Native / Hermes, process is defined but version is not.
|
|
41
|
+
// Note: we only polyfill if process already exists but is incomplete.
|
|
42
|
+
// The user is responsible for providing the process polyfill; this is just
|
|
43
|
+
// a small patch for environments (like Hermes) with partial implementations.
|
|
44
|
+
//
|
|
45
|
+
// More information: https://github.com/browserify/hash-base/issues/21#issuecomment-3476608003
|
|
46
|
+
const g = typeof globalThis !== 'undefined'
|
|
47
|
+
? globalThis
|
|
48
|
+
: typeof global !== 'undefined'
|
|
49
|
+
? global
|
|
50
|
+
: {};
|
|
51
|
+
if (typeof g.process !== 'undefined' &&
|
|
52
|
+
typeof g.process.version === 'undefined') {
|
|
53
|
+
const isDev = g['__DEV__'] === true ||
|
|
54
|
+
g.process?.env?.['NODE_ENV'] === 'development';
|
|
55
|
+
if (isDev) {
|
|
56
|
+
//only WARN while developing
|
|
57
|
+
console.warn(`[bitcoinerlab/descriptors] Polyfilled process.version (missing in this non-Node environment).
|
|
58
|
+
Learn more: https://github.com/bitcoinerlab/descriptors/blob/main/src/index.ts#L4`);
|
|
59
|
+
}
|
|
60
|
+
// @ts-expect-error Polyfill for environments missing process.version
|
|
61
|
+
global.process.version = '';
|
|
62
|
+
}
|
|
63
|
+
var networks_1 = require("./networks");
|
|
64
|
+
Object.defineProperty(exports, "networks", { enumerable: true, get: function () { return networks_1.networks; } });
|
|
65
|
+
var descriptors_1 = require("./descriptors");
|
|
66
|
+
Object.defineProperty(exports, "DescriptorsFactory", { enumerable: true, get: function () { return descriptors_1.DescriptorsFactory; } });
|
|
67
|
+
var checksum_1 = require("./checksum");
|
|
68
|
+
Object.defineProperty(exports, "checksum", { enumerable: true, get: function () { return checksum_1.DescriptorChecksum; } });
|
|
69
|
+
const signers = __importStar(require("./signers"));
|
|
70
|
+
exports.signers = signers;
|
|
71
|
+
var keyExpressions_1 = require("./keyExpressions");
|
|
72
|
+
Object.defineProperty(exports, "keyExpressionBIP32", { enumerable: true, get: function () { return keyExpressions_1.keyExpressionBIP32; } });
|
|
73
|
+
Object.defineProperty(exports, "keyExpressionLedger", { enumerable: true, get: function () { return keyExpressions_1.keyExpressionLedger; } });
|
|
74
|
+
const scriptExpressions = __importStar(require("./scriptExpressions"));
|
|
75
|
+
exports.scriptExpressions = scriptExpressions;
|
|
76
|
+
const ledger_1 = require("./ledger");
|
|
77
|
+
/** @namespace */
|
|
78
|
+
exports.ledger = {
|
|
79
|
+
/** @function */
|
|
80
|
+
getLedgerMasterFingerPrint: ledger_1.getLedgerMasterFingerPrint,
|
|
81
|
+
/** @function */
|
|
82
|
+
getLedgerXpub: ledger_1.getLedgerXpub,
|
|
83
|
+
/** @function */
|
|
84
|
+
registerLedgerWallet: ledger_1.registerLedgerWallet,
|
|
85
|
+
/** @function */
|
|
86
|
+
assertLedgerApp: ledger_1.assertLedgerApp
|
|
87
|
+
};
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import type { ECPairAPILike, BIP32APILike, BIP32InterfaceLike, ScureHDKeyLike } from './bitcoinLib';
|
|
2
|
+
import { type Network } from './networks';
|
|
3
|
+
import type { KeyInfo } from './types';
|
|
4
|
+
import { LedgerManager } from './ledger';
|
|
5
|
+
/**
|
|
6
|
+
* Parses a key expression (xpub, xprv, pubkey or wif) into {@link KeyInfo | `KeyInfo`}.
|
|
7
|
+
*
|
|
8
|
+
* For example, given this `keyExpression`: `"[d34db33f/49'/0'/0']tpubDCdxmvzJ5QBjTN8oCjjyT2V58AyZvA1fkmCeZRC75QMoaHcVP2m45Bv3hmnR7ttAwkb2UNYyoXdHVt4gwBqRrJqLUU2JrM43HippxiWpHra/1/2/3/4/*"`, this is its parsed result:
|
|
9
|
+
*
|
|
10
|
+
* ```javascript
|
|
11
|
+
* {
|
|
12
|
+
* keyExpression:
|
|
13
|
+
* "[d34db33f/49'/0'/0']tpubDCdxmvzJ5QBjTN8oCjjyT2V58AyZvA1fkmCeZRC75QMoaHcVP2m45Bv3hmnR7ttAwkb2UNYyoXdHVt4gwBqRrJqLUU2JrM43HippxiWpHra/1/2/3/4/*",
|
|
14
|
+
* keyPath: '/1/2/3/4/*',
|
|
15
|
+
* originPath: "/49'/0'/0'",
|
|
16
|
+
* path: "m/49'/0'/0'/1/2/3/4/*",
|
|
17
|
+
* // Other relevant properties of `KeyInfo`: `pubkey`, `ecpair`, `bip32`,
|
|
18
|
+
* // `privkey`, `xPub`, `xPrv`, `masterFingerprint`, etc.
|
|
19
|
+
* }
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export declare function parseKeyExpression({ keyExpression, isSegwit, isTaproot, ECPair, BIP32, network }: {
|
|
23
|
+
keyExpression: string;
|
|
24
|
+
/** @default networks.bitcoin */
|
|
25
|
+
network?: Network;
|
|
26
|
+
/**
|
|
27
|
+
* Indicates if this key expression belongs to a a SegWit output. When set,
|
|
28
|
+
* further checks are done to ensure the public key (if present in the
|
|
29
|
+
* expression) is compressed (33 bytes).
|
|
30
|
+
*/
|
|
31
|
+
isSegwit?: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Indicates if this key expression belongs to a Taproot output. For Taproot,
|
|
34
|
+
* the key must be represented as an x-only public key (32 bytes).
|
|
35
|
+
* If a 33-byte compressed pubkey is derived, it is converted to its x-only
|
|
36
|
+
* representation.
|
|
37
|
+
*/
|
|
38
|
+
isTaproot?: boolean;
|
|
39
|
+
ECPair: ECPairAPILike;
|
|
40
|
+
BIP32: BIP32APILike;
|
|
41
|
+
}): KeyInfo;
|
|
42
|
+
/**
|
|
43
|
+
* Constructs a key expression string for a Ledger device from the provided
|
|
44
|
+
* components.
|
|
45
|
+
*
|
|
46
|
+
* This function assists in crafting key expressions tailored for Ledger
|
|
47
|
+
* hardware wallets. It fetches the master fingerprint and xpub for a
|
|
48
|
+
* specified origin path and then combines them with the input parameters.
|
|
49
|
+
*
|
|
50
|
+
* For detailed understanding and examples of terms like `originPath`,
|
|
51
|
+
* `change`, and `keyPath`, refer to the documentation of
|
|
52
|
+
* {@link KeyExpressionParser}, which consists
|
|
53
|
+
* of the reverse procedure.
|
|
54
|
+
*
|
|
55
|
+
* @returns {Promise<string>} - The formed key expression for the Ledger device.
|
|
56
|
+
*/
|
|
57
|
+
export declare function keyExpressionLedger({ ledgerManager, originPath, keyPath, change, index }: {
|
|
58
|
+
ledgerManager: LedgerManager;
|
|
59
|
+
originPath: string;
|
|
60
|
+
change?: number | undefined;
|
|
61
|
+
index?: number | undefined | '*';
|
|
62
|
+
keyPath?: string | undefined;
|
|
63
|
+
}): Promise<string>;
|
|
64
|
+
/**
|
|
65
|
+
* Constructs a BIP32 key expression string from its constituent components.
|
|
66
|
+
*
|
|
67
|
+
* This function essentially performs the reverse operation of
|
|
68
|
+
* {@link KeyExpressionParser}. For detailed
|
|
69
|
+
* explanations and examples of the terms used here, refer to
|
|
70
|
+
* {@link KeyExpressionParser}.
|
|
71
|
+
*
|
|
72
|
+
* @param {Object} params - The parameters object.
|
|
73
|
+
* @param {BIP32InterfaceLike | ScureHDKeyLike} params.masterNode - Root HD node.
|
|
74
|
+
* Pass either:
|
|
75
|
+
* - a bitcoinjs {@link https://github.com/bitcoinjs/bip32 | `BIP32`} node, or
|
|
76
|
+
* - a scure {@link https://github.com/paulmillr/scure-bip32 | `HDKey`}.
|
|
77
|
+
* @param {string} params.originPath - Origin path from master, e.g. `"/84'/0'/0'"`.
|
|
78
|
+
* @param {number} [params.change] - Branch index (`0` receive, `1` change).
|
|
79
|
+
* @param {number | '*'} [params.index] - Address index or `*` for ranged descriptors.
|
|
80
|
+
* @param {string} [params.keyPath] - Full suffix path (`/change/index`) alternative to
|
|
81
|
+
* `change` + `index`.
|
|
82
|
+
* @returns {string} Descriptor key expression (e.g. `[f23f9fd2/84'/0'/0']xpub.../0/5`).
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* ```ts
|
|
86
|
+
* import * as ecc from '@bitcoinerlab/secp256k1';
|
|
87
|
+
* import { BIP32Factory } from 'bip32';
|
|
88
|
+
* import { keyExpressionBIP32 } from '@bitcoinerlab/descriptors';
|
|
89
|
+
*
|
|
90
|
+
* const BIP32 = BIP32Factory(ecc);
|
|
91
|
+
* const masterNode = BIP32.fromSeed(seedBytes);
|
|
92
|
+
* const keyExp = keyExpressionBIP32({
|
|
93
|
+
* masterNode,
|
|
94
|
+
* originPath: "/84'/0'/0'",
|
|
95
|
+
* change: 0,
|
|
96
|
+
* index: 5
|
|
97
|
+
* });
|
|
98
|
+
* ```
|
|
99
|
+
*
|
|
100
|
+
* @example
|
|
101
|
+
* ```ts
|
|
102
|
+
* import { HDKey } from '@scure/bip32';
|
|
103
|
+
* import { keyExpressionBIP32 } from '@bitcoinerlab/descriptors';
|
|
104
|
+
*
|
|
105
|
+
* const masterNode = HDKey.fromMasterSeed(seedBytes);
|
|
106
|
+
* const keyExp = keyExpressionBIP32({
|
|
107
|
+
* masterNode,
|
|
108
|
+
* originPath: "/84'/0'/0'",
|
|
109
|
+
* keyPath: '/0/*'
|
|
110
|
+
* });
|
|
111
|
+
* ```
|
|
112
|
+
*/
|
|
113
|
+
export declare function keyExpressionBIP32({ masterNode, originPath, keyPath, change, index, isPublic }: {
|
|
114
|
+
masterNode: BIP32InterfaceLike | ScureHDKeyLike;
|
|
115
|
+
originPath: string;
|
|
116
|
+
change?: number | undefined;
|
|
117
|
+
index?: number | undefined | '*';
|
|
118
|
+
keyPath?: string | undefined;
|
|
119
|
+
/**
|
|
120
|
+
* Compute an xpub or xprv
|
|
121
|
+
* @default true
|
|
122
|
+
*/
|
|
123
|
+
isPublic?: boolean;
|
|
124
|
+
}): string;
|
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright (c) 2023 Jose-Luis Landabaso - https://bitcoinerlab.com
|
|
3
|
+
// Distributed under the MIT software license
|
|
4
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
5
|
+
if (k2 === undefined) k2 = k;
|
|
6
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
7
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
8
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
9
|
+
}
|
|
10
|
+
Object.defineProperty(o, k2, desc);
|
|
11
|
+
}) : (function(o, m, k, k2) {
|
|
12
|
+
if (k2 === undefined) k2 = k;
|
|
13
|
+
o[k2] = m[k];
|
|
14
|
+
}));
|
|
15
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
16
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
17
|
+
}) : function(o, v) {
|
|
18
|
+
o["default"] = v;
|
|
19
|
+
});
|
|
20
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
21
|
+
var ownKeys = function(o) {
|
|
22
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
23
|
+
var ar = [];
|
|
24
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
25
|
+
return ar;
|
|
26
|
+
};
|
|
27
|
+
return ownKeys(o);
|
|
28
|
+
};
|
|
29
|
+
return function (mod) {
|
|
30
|
+
if (mod && mod.__esModule) return mod;
|
|
31
|
+
var result = {};
|
|
32
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
33
|
+
__setModuleDefault(result, mod);
|
|
34
|
+
return result;
|
|
35
|
+
};
|
|
36
|
+
})();
|
|
37
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
38
|
+
exports.parseKeyExpression = parseKeyExpression;
|
|
39
|
+
exports.keyExpressionLedger = keyExpressionLedger;
|
|
40
|
+
exports.keyExpressionBIP32 = keyExpressionBIP32;
|
|
41
|
+
const networks_1 = require("./networks");
|
|
42
|
+
const ledger_1 = require("./ledger");
|
|
43
|
+
const keyInterfaces_1 = require("./keyInterfaces");
|
|
44
|
+
const uint8array_tools_1 = require("uint8array-tools");
|
|
45
|
+
const RE = __importStar(require("./re"));
|
|
46
|
+
const derivePath = (node, path) => {
|
|
47
|
+
if (typeof path !== 'string') {
|
|
48
|
+
throw new Error(`Error: invalid derivation path ${path}`);
|
|
49
|
+
}
|
|
50
|
+
const parsedPath = path.replaceAll('H', "'").replaceAll('h', "'").slice(1);
|
|
51
|
+
const splitPath = parsedPath.split('/');
|
|
52
|
+
for (const element of splitPath) {
|
|
53
|
+
const unhardened = element.endsWith("'") ? element.slice(0, -1) : element;
|
|
54
|
+
if (!Number.isInteger(Number(unhardened)) ||
|
|
55
|
+
Number(unhardened) >= 0x80000000)
|
|
56
|
+
throw new Error(`Error: BIP 32 path element overflow`);
|
|
57
|
+
}
|
|
58
|
+
return node.derivePath(parsedPath);
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* Parses a key expression (xpub, xprv, pubkey or wif) into {@link KeyInfo | `KeyInfo`}.
|
|
62
|
+
*
|
|
63
|
+
* For example, given this `keyExpression`: `"[d34db33f/49'/0'/0']tpubDCdxmvzJ5QBjTN8oCjjyT2V58AyZvA1fkmCeZRC75QMoaHcVP2m45Bv3hmnR7ttAwkb2UNYyoXdHVt4gwBqRrJqLUU2JrM43HippxiWpHra/1/2/3/4/*"`, this is its parsed result:
|
|
64
|
+
*
|
|
65
|
+
* ```javascript
|
|
66
|
+
* {
|
|
67
|
+
* keyExpression:
|
|
68
|
+
* "[d34db33f/49'/0'/0']tpubDCdxmvzJ5QBjTN8oCjjyT2V58AyZvA1fkmCeZRC75QMoaHcVP2m45Bv3hmnR7ttAwkb2UNYyoXdHVt4gwBqRrJqLUU2JrM43HippxiWpHra/1/2/3/4/*",
|
|
69
|
+
* keyPath: '/1/2/3/4/*',
|
|
70
|
+
* originPath: "/49'/0'/0'",
|
|
71
|
+
* path: "m/49'/0'/0'/1/2/3/4/*",
|
|
72
|
+
* // Other relevant properties of `KeyInfo`: `pubkey`, `ecpair`, `bip32`,
|
|
73
|
+
* // `privkey`, `xPub`, `xPrv`, `masterFingerprint`, etc.
|
|
74
|
+
* }
|
|
75
|
+
* ```
|
|
76
|
+
*/
|
|
77
|
+
function parseKeyExpression({ keyExpression, isSegwit, isTaproot, ECPair, BIP32, network = networks_1.networks.bitcoin }) {
|
|
78
|
+
if (isTaproot && isSegwit !== true)
|
|
79
|
+
throw new Error(`Error: taproot key expressions require isSegwit`);
|
|
80
|
+
let pubkey; //won't be computed for ranged keyExpressions
|
|
81
|
+
let ecpair;
|
|
82
|
+
let bip32;
|
|
83
|
+
let privkey;
|
|
84
|
+
let xPub;
|
|
85
|
+
let xPrv;
|
|
86
|
+
let masterFingerprint;
|
|
87
|
+
let originPath;
|
|
88
|
+
let keyPath;
|
|
89
|
+
let path;
|
|
90
|
+
const isRanged = keyExpression.indexOf('*') !== -1;
|
|
91
|
+
const reKeyExp = isTaproot
|
|
92
|
+
? RE.reTaprootKeyExp
|
|
93
|
+
: isSegwit
|
|
94
|
+
? RE.reSegwitKeyExp
|
|
95
|
+
: RE.reNonSegwitKeyExp;
|
|
96
|
+
const rePubKey = isTaproot
|
|
97
|
+
? RE.reTaprootPubKey
|
|
98
|
+
: isSegwit
|
|
99
|
+
? RE.reSegwitPubKey
|
|
100
|
+
: RE.reNonSegwitPubKey;
|
|
101
|
+
//Validate the keyExpression:
|
|
102
|
+
const keyExpressions = keyExpression.match(reKeyExp);
|
|
103
|
+
if (keyExpressions === null || keyExpressions[0] !== keyExpression) {
|
|
104
|
+
throw new Error(`Error: expected a keyExpression but got ${keyExpression}`);
|
|
105
|
+
}
|
|
106
|
+
const reOriginAnchoredStart = RegExp(String.raw `^(${RE.reOrigin})?`); //starts with ^origin
|
|
107
|
+
const mOrigin = keyExpression.match(reOriginAnchoredStart);
|
|
108
|
+
if (mOrigin) {
|
|
109
|
+
const bareOrigin = mOrigin[0].replace(/[[\]]/g, ''); //strip the "[" and "]" in [origin]
|
|
110
|
+
const reMasterFingerprintAnchoredStart = String.raw `^(${RE.reMasterFingerprint})`;
|
|
111
|
+
const mMasterFingerprint = bareOrigin.match(reMasterFingerprintAnchoredStart);
|
|
112
|
+
const masterFingerprintHex = mMasterFingerprint
|
|
113
|
+
? mMasterFingerprint[0]
|
|
114
|
+
: '';
|
|
115
|
+
originPath = bareOrigin.replace(masterFingerprintHex, '');
|
|
116
|
+
if (masterFingerprintHex.length > 0) {
|
|
117
|
+
if (masterFingerprintHex.length !== 8)
|
|
118
|
+
throw new Error(`Error: masterFingerprint ${masterFingerprintHex} invalid for keyExpression: ${keyExpression}`);
|
|
119
|
+
masterFingerprint = (0, uint8array_tools_1.fromHex)(masterFingerprintHex);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
//Remove the origin (if it exists) and store result in actualKey
|
|
123
|
+
const actualKey = keyExpression.replace(reOriginAnchoredStart, '');
|
|
124
|
+
let mPubKey, mWIF, mXpubKey, mXprvKey;
|
|
125
|
+
//match pubkey:
|
|
126
|
+
if ((mPubKey = actualKey.match(RE.anchorStartAndEnd(rePubKey))) !== null) {
|
|
127
|
+
pubkey = (0, uint8array_tools_1.fromHex)(mPubKey[0]);
|
|
128
|
+
if (isTaproot && pubkey.length === 32)
|
|
129
|
+
//convert the xonly point to a compressed point assuming even parity
|
|
130
|
+
pubkey = (0, uint8array_tools_1.concat)([Uint8Array.from([0x02]), pubkey]);
|
|
131
|
+
ecpair = ECPair.fromPublicKey(pubkey, { network });
|
|
132
|
+
//Validate the pubkey (compressed or uncompressed)
|
|
133
|
+
if (!ECPair.isPoint(pubkey) ||
|
|
134
|
+
!(pubkey.length === 33 || pubkey.length === 65)) {
|
|
135
|
+
throw new Error(`Error: invalid pubkey`);
|
|
136
|
+
}
|
|
137
|
+
//Do an extra check in case we know this pubkey refers to a segwit input
|
|
138
|
+
//Taproot x-only keys are converted to 33-byte compressed form above.
|
|
139
|
+
if (typeof isSegwit === 'boolean' &&
|
|
140
|
+
isSegwit &&
|
|
141
|
+
pubkey.length !== 33 //Inside wpkh and wsh, only compressed public keys are permitted.
|
|
142
|
+
) {
|
|
143
|
+
throw new Error(`Error: invalid pubkey`);
|
|
144
|
+
}
|
|
145
|
+
//match WIF:
|
|
146
|
+
}
|
|
147
|
+
else if ((mWIF = actualKey.match(RE.anchorStartAndEnd(RE.reWIF))) !== null) {
|
|
148
|
+
ecpair = ECPair.fromWIF(mWIF[0], network);
|
|
149
|
+
//fromWIF will throw if the wif is not valid
|
|
150
|
+
pubkey = ecpair.publicKey;
|
|
151
|
+
privkey = ecpair.privateKey;
|
|
152
|
+
//match xpub:
|
|
153
|
+
}
|
|
154
|
+
else if ((mXpubKey = actualKey.match(RE.anchorStartAndEnd(RE.reXpubKey))) !== null) {
|
|
155
|
+
const xPubKey = mXpubKey[0];
|
|
156
|
+
xPub = xPubKey.match(RE.reXpub)?.[0];
|
|
157
|
+
if (!xPub)
|
|
158
|
+
throw new Error(`Error: xpub could not be matched`);
|
|
159
|
+
bip32 = BIP32.fromBase58(xPub, network);
|
|
160
|
+
const mPath = xPubKey.match(RE.rePath);
|
|
161
|
+
if (mPath !== null) {
|
|
162
|
+
keyPath = xPubKey.match(RE.rePath)?.[0];
|
|
163
|
+
if (!keyPath)
|
|
164
|
+
throw new Error(`Error: could not extract a path`);
|
|
165
|
+
//fromBase58 and derivePath will throw if xPub or path are not valid
|
|
166
|
+
if (!isRanged)
|
|
167
|
+
pubkey = derivePath(bip32, keyPath).publicKey;
|
|
168
|
+
}
|
|
169
|
+
else {
|
|
170
|
+
pubkey = bip32.publicKey;
|
|
171
|
+
}
|
|
172
|
+
//match xprv:
|
|
173
|
+
}
|
|
174
|
+
else if ((mXprvKey = actualKey.match(RE.anchorStartAndEnd(RE.reXprvKey))) !== null) {
|
|
175
|
+
const xPrvKey = mXprvKey[0];
|
|
176
|
+
xPrv = xPrvKey.match(RE.reXprv)?.[0];
|
|
177
|
+
if (!xPrv)
|
|
178
|
+
throw new Error(`Error: xprv could not be matched`);
|
|
179
|
+
bip32 = BIP32.fromBase58(xPrv, network);
|
|
180
|
+
xPub = bip32.neutered().toBase58();
|
|
181
|
+
const mPath = xPrvKey.match(RE.rePath);
|
|
182
|
+
if (mPath !== null) {
|
|
183
|
+
keyPath = xPrvKey.match(RE.rePath)?.[0];
|
|
184
|
+
if (!keyPath)
|
|
185
|
+
throw new Error(`Error: could not extract a path`);
|
|
186
|
+
//fromBase58 and derivePath will throw if xPrv or path are not valid
|
|
187
|
+
if (!isRanged)
|
|
188
|
+
pubkey = derivePath(bip32, keyPath).publicKey;
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
pubkey = bip32.publicKey;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
throw new Error(`Error: could not get pubkey for keyExpression ${keyExpression}`);
|
|
196
|
+
}
|
|
197
|
+
if (originPath || keyPath) {
|
|
198
|
+
path = `m${originPath ?? ''}${keyPath ?? ''}`;
|
|
199
|
+
}
|
|
200
|
+
if (pubkey !== undefined) {
|
|
201
|
+
if (isTaproot) {
|
|
202
|
+
if (pubkey.length !== 33)
|
|
203
|
+
throw new Error(`Error: invalid pubkey`);
|
|
204
|
+
}
|
|
205
|
+
else if (typeof isSegwit === 'boolean' &&
|
|
206
|
+
isSegwit &&
|
|
207
|
+
pubkey.length !== 33) {
|
|
208
|
+
throw new Error(`Error: invalid pubkey`);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
if (pubkey !== undefined && isTaproot && pubkey.length === 33)
|
|
212
|
+
// If we get a 33-byte compressed key, drop the first byte.
|
|
213
|
+
pubkey = pubkey.slice(1, 33);
|
|
214
|
+
return {
|
|
215
|
+
keyExpression,
|
|
216
|
+
...(pubkey !== undefined ? { pubkey } : {}),
|
|
217
|
+
...(ecpair !== undefined ? { ecpair } : {}),
|
|
218
|
+
...(bip32 !== undefined ? { bip32 } : {}),
|
|
219
|
+
...(privkey !== undefined ? { privkey } : {}),
|
|
220
|
+
...(xPub !== undefined ? { xPub } : {}),
|
|
221
|
+
...(xPrv !== undefined ? { xPrv } : {}),
|
|
222
|
+
...(masterFingerprint !== undefined ? { masterFingerprint } : {}),
|
|
223
|
+
...(originPath !== undefined && originPath !== '' ? { originPath } : {}),
|
|
224
|
+
...(keyPath !== undefined && keyPath !== '' ? { keyPath } : {}),
|
|
225
|
+
...(path !== undefined ? { path } : {})
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
function assertChangeIndexKeyPath({ change, index, keyPath }) {
|
|
229
|
+
if (!((change === undefined && index === undefined) ||
|
|
230
|
+
(change !== undefined && index !== undefined)))
|
|
231
|
+
throw new Error(`Error: Pass change and index or neither`);
|
|
232
|
+
if ((change !== undefined) === (keyPath !== undefined))
|
|
233
|
+
throw new Error(`Error: Pass either change and index or a keyPath`);
|
|
234
|
+
}
|
|
235
|
+
async function keyExpressionLedger({ ledgerManager, originPath, keyPath, change, index }) {
|
|
236
|
+
assertChangeIndexKeyPath({ change, index, keyPath });
|
|
237
|
+
const masterFingerprint = await (0, ledger_1.getLedgerMasterFingerPrint)({
|
|
238
|
+
ledgerManager
|
|
239
|
+
});
|
|
240
|
+
const origin = `[${(0, uint8array_tools_1.toHex)(masterFingerprint)}${originPath}]`;
|
|
241
|
+
const xpub = await (0, ledger_1.getLedgerXpub)({ originPath, ledgerManager });
|
|
242
|
+
const keyRoot = `${origin}${xpub}`;
|
|
243
|
+
if (keyPath !== undefined)
|
|
244
|
+
return `${keyRoot}${keyPath}`;
|
|
245
|
+
else
|
|
246
|
+
return `${keyRoot}/${change}/${index}`;
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Constructs a BIP32 key expression string from its constituent components.
|
|
250
|
+
*
|
|
251
|
+
* This function essentially performs the reverse operation of
|
|
252
|
+
* {@link KeyExpressionParser}. For detailed
|
|
253
|
+
* explanations and examples of the terms used here, refer to
|
|
254
|
+
* {@link KeyExpressionParser}.
|
|
255
|
+
*
|
|
256
|
+
* @param {Object} params - The parameters object.
|
|
257
|
+
* @param {BIP32InterfaceLike | ScureHDKeyLike} params.masterNode - Root HD node.
|
|
258
|
+
* Pass either:
|
|
259
|
+
* - a bitcoinjs {@link https://github.com/bitcoinjs/bip32 | `BIP32`} node, or
|
|
260
|
+
* - a scure {@link https://github.com/paulmillr/scure-bip32 | `HDKey`}.
|
|
261
|
+
* @param {string} params.originPath - Origin path from master, e.g. `"/84'/0'/0'"`.
|
|
262
|
+
* @param {number} [params.change] - Branch index (`0` receive, `1` change).
|
|
263
|
+
* @param {number | '*'} [params.index] - Address index or `*` for ranged descriptors.
|
|
264
|
+
* @param {string} [params.keyPath] - Full suffix path (`/change/index`) alternative to
|
|
265
|
+
* `change` + `index`.
|
|
266
|
+
* @returns {string} Descriptor key expression (e.g. `[f23f9fd2/84'/0'/0']xpub.../0/5`).
|
|
267
|
+
*
|
|
268
|
+
* @example
|
|
269
|
+
* ```ts
|
|
270
|
+
* import * as ecc from '@bitcoinerlab/secp256k1';
|
|
271
|
+
* import { BIP32Factory } from 'bip32';
|
|
272
|
+
* import { keyExpressionBIP32 } from '@bitcoinerlab/descriptors';
|
|
273
|
+
*
|
|
274
|
+
* const BIP32 = BIP32Factory(ecc);
|
|
275
|
+
* const masterNode = BIP32.fromSeed(seedBytes);
|
|
276
|
+
* const keyExp = keyExpressionBIP32({
|
|
277
|
+
* masterNode,
|
|
278
|
+
* originPath: "/84'/0'/0'",
|
|
279
|
+
* change: 0,
|
|
280
|
+
* index: 5
|
|
281
|
+
* });
|
|
282
|
+
* ```
|
|
283
|
+
*
|
|
284
|
+
* @example
|
|
285
|
+
* ```ts
|
|
286
|
+
* import { HDKey } from '@scure/bip32';
|
|
287
|
+
* import { keyExpressionBIP32 } from '@bitcoinerlab/descriptors';
|
|
288
|
+
*
|
|
289
|
+
* const masterNode = HDKey.fromMasterSeed(seedBytes);
|
|
290
|
+
* const keyExp = keyExpressionBIP32({
|
|
291
|
+
* masterNode,
|
|
292
|
+
* originPath: "/84'/0'/0'",
|
|
293
|
+
* keyPath: '/0/*'
|
|
294
|
+
* });
|
|
295
|
+
* ```
|
|
296
|
+
*/
|
|
297
|
+
function keyExpressionBIP32({ masterNode, originPath, keyPath, change, index, isPublic = true }) {
|
|
298
|
+
masterNode = (0, keyInterfaces_1.toBIP32Interface)(masterNode);
|
|
299
|
+
assertChangeIndexKeyPath({ change, index, keyPath });
|
|
300
|
+
const masterFingerprint = masterNode.fingerprint;
|
|
301
|
+
const origin = `[${(0, uint8array_tools_1.toHex)(masterFingerprint)}${originPath}]`;
|
|
302
|
+
const xpub = isPublic
|
|
303
|
+
? masterNode.derivePath(`m${originPath}`).neutered().toBase58().toString()
|
|
304
|
+
: masterNode.derivePath(`m${originPath}`).toBase58().toString();
|
|
305
|
+
const keyRoot = `${origin}${xpub}`;
|
|
306
|
+
if (keyPath !== undefined)
|
|
307
|
+
return `${keyRoot}${keyPath}`;
|
|
308
|
+
else
|
|
309
|
+
return `${keyRoot}/${change}/${index}`;
|
|
310
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { type ECPairInterfaceLike, type BIP32InterfaceLike, type ScureHDKeyLike } from './bitcoinLib';
|
|
2
|
+
/** @internal */
|
|
3
|
+
export declare function toECPairInterface(ecpair: ECPairInterfaceLike | Uint8Array): ECPairInterfaceLike;
|
|
4
|
+
/** @internal */
|
|
5
|
+
export declare function toBIP32Interface(node: BIP32InterfaceLike | ScureHDKeyLike): BIP32InterfaceLike;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright (c) 2026 Jose-Luis Landabaso - https://bitcoinerlab.com
|
|
3
|
+
// Distributed under the MIT software license
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.toECPairInterface = toECPairInterface;
|
|
6
|
+
exports.toBIP32Interface = toBIP32Interface;
|
|
7
|
+
function isScureHDKey(node) {
|
|
8
|
+
const candidate = node;
|
|
9
|
+
return (typeof candidate.fingerprint === 'number' &&
|
|
10
|
+
typeof candidate.derive === 'function' &&
|
|
11
|
+
typeof candidate.deriveChild === 'function' &&
|
|
12
|
+
typeof candidate.publicExtendedKey === 'string' &&
|
|
13
|
+
typeof candidate.privateExtendedKey === 'string');
|
|
14
|
+
}
|
|
15
|
+
/** @internal */
|
|
16
|
+
function toECPairInterface(ecpair) {
|
|
17
|
+
if (ecpair instanceof Uint8Array) {
|
|
18
|
+
try {
|
|
19
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
20
|
+
const { wrapScurePrivateKey } = require('./adapters/scureKeys');
|
|
21
|
+
return wrapScurePrivateKey(ecpair);
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
throw new Error('Failed to load scure key adapter. ' +
|
|
25
|
+
'Make sure @noble/curves is installed as a peer dependency. ' +
|
|
26
|
+
'Original error: ' +
|
|
27
|
+
(error instanceof Error ? error.message : String(error)));
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
// Already a bitcoinjs-lib compatible ECPairInterface
|
|
31
|
+
return ecpair;
|
|
32
|
+
}
|
|
33
|
+
/** @internal */
|
|
34
|
+
function toBIP32Interface(node) {
|
|
35
|
+
if (isScureHDKey(node)) {
|
|
36
|
+
try {
|
|
37
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
38
|
+
const { wrapScureHDKey } = require('./adapters/scureKeys');
|
|
39
|
+
return wrapScureHDKey(node);
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
throw new Error('Failed to load scure key adapter. ' +
|
|
43
|
+
'Make sure @scure/bip32 is installed as a peer dependency. ' +
|
|
44
|
+
'Original error: ' +
|
|
45
|
+
(error instanceof Error ? error.message : String(error)));
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
// Already a bitcoinjs-lib compatible Bip32Interface
|
|
49
|
+
return node;
|
|
50
|
+
}
|