@btc-vision/transaction 1.7.0 → 1.7.1
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/browser/index.js +1 -1
- package/browser/src/_version.d.ts +1 -1
- package/browser/src/mnemonic/BIPStandard.d.ts +8 -0
- package/browser/src/mnemonic/Mnemonic.d.ts +7 -2
- package/browser/src/opnet.d.ts +1 -0
- package/build/_version.d.ts +1 -1
- package/build/_version.js +1 -1
- package/build/mnemonic/BIPStandard.d.ts +8 -0
- package/build/mnemonic/BIPStandard.js +24 -0
- package/build/mnemonic/Mnemonic.d.ts +7 -2
- package/build/mnemonic/Mnemonic.js +48 -6
- package/build/opnet.d.ts +1 -0
- package/build/opnet.js +1 -0
- package/documentation/README.md +32 -0
- package/documentation/quantum-support/01-introduction.md +88 -0
- package/documentation/quantum-support/02-mnemonic-and-wallet.md +445 -0
- package/documentation/quantum-support/03-address-generation.md +329 -0
- package/documentation/quantum-support/04-message-signing.md +623 -0
- package/documentation/quantum-support/05-address-verification.md +307 -0
- package/documentation/quantum-support/README.md +65 -0
- package/package.json +1 -1
- package/src/_version.ts +1 -1
- package/src/mnemonic/BIPStandard.ts +92 -0
- package/src/mnemonic/Mnemonic.ts +133 -8
- package/src/opnet.ts +1 -0
- package/test/derivePath.test.ts +280 -1
- package/doc/README.md +0 -0
- /package/{doc → documentation}/addresses/P2OP.md +0 -0
- /package/{doc → documentation}/addresses/P2WDA.md +0 -0
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
# Address Verification Guide
|
|
2
|
+
|
|
3
|
+
## Table of Contents
|
|
4
|
+
- [ML-DSA Public Key Validation](#ml-dsa-public-key-validation)
|
|
5
|
+
- [Address Type Detection](#address-type-detection)
|
|
6
|
+
- [Classical Address Validation](#classical-address-validation)
|
|
7
|
+
- [Complete Validation Example](#complete-validation-example)
|
|
8
|
+
|
|
9
|
+
## ML-DSA Public Key Validation
|
|
10
|
+
|
|
11
|
+
### Validating ML-DSA Public Keys
|
|
12
|
+
|
|
13
|
+
The `AddressVerificator` provides methods to validate ML-DSA public keys and determine their security level:
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import { AddressVerificator, MLDSASecurityLevel } from '@btc-vision/transaction';
|
|
17
|
+
|
|
18
|
+
// Valid ML-DSA-44 public key (1312 bytes)
|
|
19
|
+
const level2Key = Buffer.alloc(1312);
|
|
20
|
+
const level2Check = AddressVerificator.isValidMLDSAPublicKey(level2Key);
|
|
21
|
+
console.log('LEVEL2 valid:', level2Check); // MLDSASecurityLevel.LEVEL2
|
|
22
|
+
|
|
23
|
+
// Valid ML-DSA-65 public key (1952 bytes)
|
|
24
|
+
const level3Key = Buffer.alloc(1952);
|
|
25
|
+
const level3Check = AddressVerificator.isValidMLDSAPublicKey(level3Key);
|
|
26
|
+
console.log('LEVEL3 valid:', level3Check); // MLDSASecurityLevel.LEVEL3
|
|
27
|
+
|
|
28
|
+
// Valid ML-DSA-87 public key (2592 bytes)
|
|
29
|
+
const level5Key = Buffer.alloc(2592);
|
|
30
|
+
const level5Check = AddressVerificator.isValidMLDSAPublicKey(level5Key);
|
|
31
|
+
console.log('LEVEL5 valid:', level5Check); // MLDSASecurityLevel.LEVEL5
|
|
32
|
+
|
|
33
|
+
// Invalid length
|
|
34
|
+
const invalidKey = Buffer.alloc(1000);
|
|
35
|
+
const invalidCheck = AddressVerificator.isValidMLDSAPublicKey(invalidKey);
|
|
36
|
+
console.log('Invalid:', invalidCheck); // null
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Input Format Support
|
|
40
|
+
|
|
41
|
+
Validation supports multiple input formats:
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
import { AddressVerificator, MLDSASecurityLevel } from '@btc-vision/transaction';
|
|
45
|
+
|
|
46
|
+
// Hex string (with 0x prefix)
|
|
47
|
+
const hexWith0x = '0x' + 'a'.repeat(2624); // 1312 bytes in hex
|
|
48
|
+
const check1 = AddressVerificator.isValidMLDSAPublicKey(hexWith0x);
|
|
49
|
+
console.log('Hex with 0x:', check1); // MLDSASecurityLevel.LEVEL2
|
|
50
|
+
|
|
51
|
+
// Hex string (without 0x prefix)
|
|
52
|
+
const hexWithout0x = 'a'.repeat(2624);
|
|
53
|
+
const check2 = AddressVerificator.isValidMLDSAPublicKey(hexWithout0x);
|
|
54
|
+
console.log('Hex without 0x:', check2); // MLDSASecurityLevel.LEVEL2
|
|
55
|
+
|
|
56
|
+
// Buffer
|
|
57
|
+
const buffer = Buffer.alloc(1312);
|
|
58
|
+
const check3 = AddressVerificator.isValidMLDSAPublicKey(buffer);
|
|
59
|
+
console.log('Buffer:', check3); // MLDSASecurityLevel.LEVEL2
|
|
60
|
+
|
|
61
|
+
// Uint8Array
|
|
62
|
+
const uint8Array = new Uint8Array(1312);
|
|
63
|
+
const check4 = AddressVerificator.isValidMLDSAPublicKey(uint8Array);
|
|
64
|
+
console.log('Uint8Array:', check4); // MLDSASecurityLevel.LEVEL2
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Validating Wallet Keys
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
import { Mnemonic, AddressVerificator, MLDSASecurityLevel } from '@btc-vision/transaction';
|
|
71
|
+
import { networks } from '@btc-vision/bitcoin';
|
|
72
|
+
|
|
73
|
+
// Generate wallet
|
|
74
|
+
const mnemonic = Mnemonic.generate(undefined, '', networks.bitcoin, MLDSASecurityLevel.LEVEL2);
|
|
75
|
+
const wallet = mnemonic.derive(0);
|
|
76
|
+
|
|
77
|
+
// Validate quantum public key
|
|
78
|
+
const quantumKeyHex = wallet.quantumPublicKeyHex;
|
|
79
|
+
const securityLevel = AddressVerificator.isValidMLDSAPublicKey(quantumKeyHex);
|
|
80
|
+
|
|
81
|
+
console.log('Quantum key valid:', securityLevel !== null);
|
|
82
|
+
console.log('Security level:', securityLevel); // MLDSASecurityLevel.LEVEL2
|
|
83
|
+
console.log('Expected:', wallet.securityLevel); // MLDSASecurityLevel.LEVEL2
|
|
84
|
+
console.log('Match:', securityLevel === wallet.securityLevel); // true
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Error Cases
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
// Empty string
|
|
91
|
+
console.log(AddressVerificator.isValidMLDSAPublicKey('')); // null
|
|
92
|
+
|
|
93
|
+
// Empty Buffer
|
|
94
|
+
console.log(AddressVerificator.isValidMLDSAPublicKey(Buffer.alloc(0))); // null
|
|
95
|
+
|
|
96
|
+
// Invalid hex
|
|
97
|
+
console.log(AddressVerificator.isValidMLDSAPublicKey('not hex')); // null
|
|
98
|
+
|
|
99
|
+
// Wrong length (classical key size)
|
|
100
|
+
const classicalKey = Buffer.alloc(33);
|
|
101
|
+
console.log(AddressVerificator.isValidMLDSAPublicKey(classicalKey)); // null
|
|
102
|
+
|
|
103
|
+
// Wrong length (arbitrary size)
|
|
104
|
+
const wrongSize = Buffer.alloc(1500);
|
|
105
|
+
console.log(AddressVerificator.isValidMLDSAPublicKey(wrongSize)); // null
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Address Type Detection
|
|
109
|
+
|
|
110
|
+
### Detecting Address Types
|
|
111
|
+
|
|
112
|
+
```typescript
|
|
113
|
+
import { AddressVerificator, AddressTypes } from '@btc-vision/transaction';
|
|
114
|
+
import { networks } from '@btc-vision/bitcoin';
|
|
115
|
+
|
|
116
|
+
const wallet = mnemonic.derive(0);
|
|
117
|
+
|
|
118
|
+
// P2TR Detection
|
|
119
|
+
const p2tr = wallet.p2tr;
|
|
120
|
+
const p2trType = AddressVerificator.detectAddressType(p2tr, networks.bitcoin);
|
|
121
|
+
console.log('P2TR type:', p2trType); // AddressTypes.P2TR
|
|
122
|
+
|
|
123
|
+
// P2WPKH Detection
|
|
124
|
+
const p2wpkh = wallet.p2wpkh;
|
|
125
|
+
const p2wpkhType = AddressVerificator.detectAddressType(p2wpkh, networks.bitcoin);
|
|
126
|
+
console.log('P2WPKH type:', p2wpkhType); // AddressTypes.P2WPKH
|
|
127
|
+
|
|
128
|
+
// P2PKH Detection
|
|
129
|
+
const p2pkh = wallet.p2pkh;
|
|
130
|
+
const p2pkhType = AddressVerificator.detectAddressType(p2pkh, networks.bitcoin);
|
|
131
|
+
console.log('P2PKH type:', p2pkhType); // AddressTypes.P2PKH
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Available Address Types
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
enum AddressTypes {
|
|
138
|
+
P2PKH = 'P2PKH', // Legacy (1...)
|
|
139
|
+
P2SH_OR_P2SH_P2WPKH = 'P2SH_OR_P2SH-P2WPKH', // Script hash (3...)
|
|
140
|
+
P2PK = 'P2PK', // Public key
|
|
141
|
+
P2TR = 'P2TR', // Taproot (bc1p...)
|
|
142
|
+
P2WPKH = 'P2WPKH', // SegWit (bc1q...)
|
|
143
|
+
P2WSH = 'P2WSH', // SegWit script (bc1q...)
|
|
144
|
+
P2WDA = 'P2WDA', // Witness data auth
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Distinguishing Similar Addresses
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
const wallet = mnemonic.derive(0);
|
|
152
|
+
|
|
153
|
+
// P2TR vs P2WPKH (both Bech32 formats)
|
|
154
|
+
const p2tr = wallet.p2tr;
|
|
155
|
+
const p2wpkh = wallet.p2wpkh;
|
|
156
|
+
|
|
157
|
+
const p2trType = AddressVerificator.detectAddressType(p2tr, networks.bitcoin);
|
|
158
|
+
const p2wpkhType = AddressVerificator.detectAddressType(p2wpkh, networks.bitcoin);
|
|
159
|
+
|
|
160
|
+
console.log('P2TR detected as:', p2trType); // AddressTypes.P2TR
|
|
161
|
+
console.log('P2WPKH detected as:', p2wpkhType); // AddressTypes.P2WPKH
|
|
162
|
+
console.log('Different types:', p2trType !== p2wpkhType); // true
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Network-Specific Detection
|
|
166
|
+
|
|
167
|
+
```typescript
|
|
168
|
+
// Mainnet address on mainnet
|
|
169
|
+
const mainnetAddr = wallet.p2tr;
|
|
170
|
+
const mainnetDetect = AddressVerificator.detectAddressType(mainnetAddr, networks.bitcoin);
|
|
171
|
+
console.log('Mainnet on mainnet:', mainnetDetect); // AddressTypes.P2TR
|
|
172
|
+
|
|
173
|
+
// Mainnet address on wrong network
|
|
174
|
+
const wrongNetwork = AddressVerificator.detectAddressType(mainnetAddr, networks.testnet);
|
|
175
|
+
console.log('Mainnet on testnet:', wrongNetwork); // null
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## Classical Address Validation
|
|
179
|
+
|
|
180
|
+
### Validating Classical Public Keys
|
|
181
|
+
|
|
182
|
+
```typescript
|
|
183
|
+
import { AddressVerificator } from '@btc-vision/transaction';
|
|
184
|
+
import { networks } from '@btc-vision/bitcoin';
|
|
185
|
+
|
|
186
|
+
const wallet = mnemonic.derive(0);
|
|
187
|
+
|
|
188
|
+
// Validate compressed public key (33 bytes)
|
|
189
|
+
const compressedKey = wallet.toPublicKeyHex();
|
|
190
|
+
const isValid = AddressVerificator.isValidPublicKey(compressedKey, networks.bitcoin);
|
|
191
|
+
console.log('Compressed key valid:', isValid); // true
|
|
192
|
+
|
|
193
|
+
// Validate uncompressed public key (65 bytes)
|
|
194
|
+
const uncompressedKey = wallet.toUncompressedPublicKey().toString('hex');
|
|
195
|
+
const isUncompressedValid = AddressVerificator.isValidPublicKey(uncompressedKey, networks.bitcoin);
|
|
196
|
+
console.log('Uncompressed key valid:', isUncompressedValid); // true
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### Validating Other Address Types
|
|
200
|
+
|
|
201
|
+
```typescript
|
|
202
|
+
// P2TR validation
|
|
203
|
+
const p2tr = wallet.p2tr;
|
|
204
|
+
const isP2TRValid = AddressVerificator.isValidP2TRAddress(p2tr, networks.bitcoin);
|
|
205
|
+
console.log('P2TR valid:', isP2TRValid); // true
|
|
206
|
+
|
|
207
|
+
// P2WPKH validation
|
|
208
|
+
const p2wpkh = wallet.p2wpkh;
|
|
209
|
+
const isP2WPKHValid = AddressVerificator.isP2WPKHAddress(p2wpkh, networks.bitcoin);
|
|
210
|
+
console.log('P2WPKH valid:', isP2WPKHValid); // true
|
|
211
|
+
|
|
212
|
+
// P2PKH or P2SH validation
|
|
213
|
+
const p2pkh = wallet.p2pkh;
|
|
214
|
+
const isLegacyValid = AddressVerificator.isP2PKHOrP2SH(p2pkh, networks.bitcoin);
|
|
215
|
+
console.log('Legacy valid:', isLegacyValid); // true
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
## Complete Validation Example
|
|
219
|
+
|
|
220
|
+
```typescript
|
|
221
|
+
import {
|
|
222
|
+
Mnemonic,
|
|
223
|
+
AddressVerificator,
|
|
224
|
+
AddressTypes,
|
|
225
|
+
MLDSASecurityLevel,
|
|
226
|
+
} from '@btc-vision/transaction';
|
|
227
|
+
import { networks } from '@btc-vision/bitcoin';
|
|
228
|
+
|
|
229
|
+
// Generate wallet
|
|
230
|
+
const mnemonic = Mnemonic.generate(undefined, '', networks.bitcoin, MLDSASecurityLevel.LEVEL2);
|
|
231
|
+
const wallet = mnemonic.derive(0);
|
|
232
|
+
|
|
233
|
+
console.log('=== ML-DSA Public Key Validation ===');
|
|
234
|
+
|
|
235
|
+
// Validate quantum public key
|
|
236
|
+
const quantumKeyHex = wallet.quantumPublicKeyHex;
|
|
237
|
+
const quantumKeyBuffer = wallet.quantumPublicKey;
|
|
238
|
+
|
|
239
|
+
const securityLevelFromHex = AddressVerificator.isValidMLDSAPublicKey(quantumKeyHex);
|
|
240
|
+
const securityLevelFromBuffer = AddressVerificator.isValidMLDSAPublicKey(quantumKeyBuffer);
|
|
241
|
+
|
|
242
|
+
console.log('Hex validation:', securityLevelFromHex); // MLDSASecurityLevel.LEVEL2
|
|
243
|
+
console.log('Buffer validation:', securityLevelFromBuffer); // MLDSASecurityLevel.LEVEL2
|
|
244
|
+
console.log('Matches wallet:', securityLevelFromHex === wallet.securityLevel); // true
|
|
245
|
+
|
|
246
|
+
console.log('\n=== Address Type Detection ===');
|
|
247
|
+
|
|
248
|
+
// Detect all address types
|
|
249
|
+
const addresses = {
|
|
250
|
+
p2tr: wallet.p2tr,
|
|
251
|
+
p2wpkh: wallet.p2wpkh,
|
|
252
|
+
p2pkh: wallet.p2pkh,
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
for (const [name, addr] of Object.entries(addresses)) {
|
|
256
|
+
const type = AddressVerificator.detectAddressType(addr, networks.bitcoin);
|
|
257
|
+
console.log(`${name}: ${type}`);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
console.log('\n=== Classical Key Validation ===');
|
|
261
|
+
|
|
262
|
+
// Validate classical public key
|
|
263
|
+
const classicalKey = wallet.toPublicKeyHex();
|
|
264
|
+
const isClassicalValid = AddressVerificator.isValidPublicKey(classicalKey, networks.bitcoin);
|
|
265
|
+
|
|
266
|
+
console.log('Classical key:', classicalKey);
|
|
267
|
+
console.log('Classical valid:', isClassicalValid); // true
|
|
268
|
+
|
|
269
|
+
console.log('\n=== Cross-Network Validation ===');
|
|
270
|
+
|
|
271
|
+
// Test network mismatch
|
|
272
|
+
const mainnetP2TR = wallet.p2tr;
|
|
273
|
+
const testnetMnemonic = Mnemonic.generate(undefined, '', networks.testnet, MLDSASecurityLevel.LEVEL2);
|
|
274
|
+
const testnetWallet = testnetMnemonic.derive(0);
|
|
275
|
+
const testnetP2TR = testnetWallet.p2tr;
|
|
276
|
+
|
|
277
|
+
const mainnetType = AddressVerificator.detectAddressType(mainnetP2TR, networks.bitcoin);
|
|
278
|
+
const wrongNetworkType = AddressVerificator.detectAddressType(mainnetP2TR, networks.testnet);
|
|
279
|
+
|
|
280
|
+
console.log('Mainnet P2TR on mainnet:', mainnetType); // AddressTypes.P2TR
|
|
281
|
+
console.log('Mainnet P2TR on testnet:', wrongNetworkType); // null
|
|
282
|
+
|
|
283
|
+
console.log('\n=== Complete Wallet Validation ===');
|
|
284
|
+
|
|
285
|
+
function validateWallet(wallet: any, network: any): boolean {
|
|
286
|
+
// Validate quantum key
|
|
287
|
+
const quantumValid = AddressVerificator.isValidMLDSAPublicKey(
|
|
288
|
+
wallet.quantumPublicKey
|
|
289
|
+
) !== null;
|
|
290
|
+
|
|
291
|
+
// Validate classical key
|
|
292
|
+
const classicalValid = AddressVerificator.isValidPublicKey(
|
|
293
|
+
wallet.toPublicKeyHex(),
|
|
294
|
+
network
|
|
295
|
+
);
|
|
296
|
+
|
|
297
|
+
return quantumValid && classicalValid;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
const isWalletValid = validateWallet(wallet, networks.bitcoin);
|
|
301
|
+
console.log('Complete wallet validation:', isWalletValid); // true
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
## Next Steps
|
|
305
|
+
|
|
306
|
+
- [Message Signing](./04-message-signing.md) - Sign and verify messages
|
|
307
|
+
- [Introduction](./01-introduction.md) - Back to overview
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# OPNet ML-DSA Quantum Support Documentation
|
|
2
|
+
|
|
3
|
+
Welcome to the OPNet ML-DSA (Post-Quantum Cryptography) documentation! This guide will help you integrate quantum-resistant signatures and addresses into your OPNet applications.
|
|
4
|
+
|
|
5
|
+
## Documentation Index
|
|
6
|
+
|
|
7
|
+
### [1. Introduction to ML-DSA](./01-introduction.md)
|
|
8
|
+
Learn about ML-DSA (Module-Lattice-Based Digital Signature Algorithm), the hybrid quantum-classical architecture, and when to use quantum-resistant cryptography.
|
|
9
|
+
|
|
10
|
+
**Topics covered:**
|
|
11
|
+
- What is ML-DSA and post-quantum cryptography
|
|
12
|
+
- Three security levels: **LEVEL2 (BIP360 RECOMMENDED DEFAULT)**, LEVEL3, LEVEL5
|
|
13
|
+
- Hybrid classical + quantum architecture
|
|
14
|
+
- Quick start guide
|
|
15
|
+
- When to use ML-DSA vs classical crypto
|
|
16
|
+
|
|
17
|
+
### [2. Mnemonic & Wallet Management](./02-mnemonic-and-wallet.md)
|
|
18
|
+
Complete guide to generating and managing quantum-resistant wallets using BIP39 mnemonics and BIP360 quantum key derivation.
|
|
19
|
+
|
|
20
|
+
**Topics covered:**
|
|
21
|
+
- Generating new mnemonics with quantum support
|
|
22
|
+
- Loading existing mnemonics
|
|
23
|
+
- Deriving wallets (single and multiple)
|
|
24
|
+
- Wallet properties (classical and quantum keys)
|
|
25
|
+
- Security best practices
|
|
26
|
+
- Network awareness
|
|
27
|
+
- Advanced usage
|
|
28
|
+
|
|
29
|
+
### [3. Address Generation](./03-address-generation.md)
|
|
30
|
+
Learn to generate P2OP and classical Bitcoin addresses for all networks.
|
|
31
|
+
|
|
32
|
+
**Topics covered:**
|
|
33
|
+
- P2OP addresses (Pay-to-OPNet, for contract addresses)
|
|
34
|
+
- Classical addresses (P2TR, P2WPKH, P2PKH, P2SH)
|
|
35
|
+
- P2WDA (Witness Data Authentication)
|
|
36
|
+
- Network support (mainnet, testnet, regtest)
|
|
37
|
+
- Address comparison and ordering
|
|
38
|
+
- Time-locked addresses (CSV)
|
|
39
|
+
- Address caching
|
|
40
|
+
|
|
41
|
+
### [4. Message Signing](./04-message-signing.md)
|
|
42
|
+
Sign and verify messages using ML-DSA (quantum) and Schnorr (classical) signatures. Includes detailed explanation of `QuantumBIP32Factory.fromPublicKey()` usage for signature verification.
|
|
43
|
+
|
|
44
|
+
**Topics covered:**
|
|
45
|
+
- ML-DSA message signing and verification
|
|
46
|
+
- Schnorr message signing
|
|
47
|
+
- Multiple input formats (string, Buffer, Uint8Array, hex)
|
|
48
|
+
- Cross-format verification
|
|
49
|
+
- Tweaked signatures for Taproot
|
|
50
|
+
- Message hashing (SHA-256)
|
|
51
|
+
- Best practices
|
|
52
|
+
|
|
53
|
+
### [5. Address Verification](./05-address-verification.md)
|
|
54
|
+
Validate ML-DSA public keys and detect classical address types.
|
|
55
|
+
|
|
56
|
+
**Topics covered:**
|
|
57
|
+
- ML-DSA public key validation
|
|
58
|
+
- Address type detection
|
|
59
|
+
- Classical address validation
|
|
60
|
+
- Network-specific validation
|
|
61
|
+
- Complete validation examples
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
**Ready to get started?** Begin with the [Introduction](./01-introduction.md)!
|
package/package.json
CHANGED
package/src/_version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = '1.7.
|
|
1
|
+
export const version = '1.7.1';
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BIP (Bitcoin Improvement Proposal) derivation path standards
|
|
3
|
+
*
|
|
4
|
+
* These define standardized derivation paths for different address types
|
|
5
|
+
* in hierarchical deterministic (HD) wallets.
|
|
6
|
+
*
|
|
7
|
+
* @see https://github.com/bitcoin/bips
|
|
8
|
+
*/
|
|
9
|
+
export enum BIPStandard {
|
|
10
|
+
/**
|
|
11
|
+
* BIP44: Multi-Account Hierarchy for Deterministic Wallets
|
|
12
|
+
*
|
|
13
|
+
* Path: m/44'/coin_type'/account'/change/address_index
|
|
14
|
+
* Original standard for P2PKH (legacy) addresses
|
|
15
|
+
* Widely used by wallets like Unisat for all address types
|
|
16
|
+
*
|
|
17
|
+
* @see https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki
|
|
18
|
+
*/
|
|
19
|
+
BIP44 = 44,
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* BIP49: Derivation scheme for P2WPKH-nested-in-P2SH based accounts
|
|
23
|
+
*
|
|
24
|
+
* Path: m/49'/coin_type'/account'/change/address_index
|
|
25
|
+
* For wrapped SegWit addresses (P2SH-P2WPKH starting with '3')
|
|
26
|
+
*
|
|
27
|
+
* @see https://github.com/bitcoin/bips/blob/master/bip-0049.mediawiki
|
|
28
|
+
*/
|
|
29
|
+
BIP49 = 49,
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* BIP84: Derivation scheme for P2WPKH based accounts
|
|
33
|
+
*
|
|
34
|
+
* Path: m/84'/coin_type'/account'/change/address_index
|
|
35
|
+
* For native SegWit addresses (P2WPKH starting with 'bc1q')
|
|
36
|
+
* DEFAULT for this library
|
|
37
|
+
*
|
|
38
|
+
* @see https://github.com/bitcoin/bips/blob/master/bip-0084.mediawiki
|
|
39
|
+
*/
|
|
40
|
+
BIP84 = 84,
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* BIP86: Derivation scheme for P2TR based accounts
|
|
44
|
+
*
|
|
45
|
+
* Path: m/86'/coin_type'/account'/change/address_index
|
|
46
|
+
* For Taproot addresses (P2TR starting with 'bc1p')
|
|
47
|
+
*
|
|
48
|
+
* @see https://github.com/bitcoin/bips/blob/master/bip-0086.mediawiki
|
|
49
|
+
*/
|
|
50
|
+
BIP86 = 86,
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Get a human-readable description of a BIP standard
|
|
55
|
+
*
|
|
56
|
+
* @param standard - The BIP standard
|
|
57
|
+
* @returns A description of the standard and its typical use case
|
|
58
|
+
*/
|
|
59
|
+
export function getBIPDescription(standard: BIPStandard): string {
|
|
60
|
+
switch (standard) {
|
|
61
|
+
case BIPStandard.BIP44:
|
|
62
|
+
return 'BIP44: Legacy addresses (P2PKH), widely used by Unisat and other wallets';
|
|
63
|
+
case BIPStandard.BIP49:
|
|
64
|
+
return 'BIP49: Wrapped SegWit addresses (P2SH-P2WPKH)';
|
|
65
|
+
case BIPStandard.BIP84:
|
|
66
|
+
return 'BIP84: Native SegWit addresses (P2WPKH) - DEFAULT';
|
|
67
|
+
case BIPStandard.BIP86:
|
|
68
|
+
return 'BIP86: Taproot addresses (P2TR)';
|
|
69
|
+
default:
|
|
70
|
+
return 'Unknown BIP standard';
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Build a derivation path for a given BIP standard
|
|
76
|
+
*
|
|
77
|
+
* @param standard - The BIP standard to use
|
|
78
|
+
* @param coinType - The coin type (0 for mainnet, 1 for testnet/regtest)
|
|
79
|
+
* @param account - The account index
|
|
80
|
+
* @param change - The change index (0 for receiving, 1 for change)
|
|
81
|
+
* @param addressIndex - The address index
|
|
82
|
+
* @returns The full derivation path
|
|
83
|
+
*/
|
|
84
|
+
export function buildBIPPath(
|
|
85
|
+
standard: BIPStandard,
|
|
86
|
+
coinType: number,
|
|
87
|
+
account: number,
|
|
88
|
+
change: number,
|
|
89
|
+
addressIndex: number,
|
|
90
|
+
): string {
|
|
91
|
+
return `m/${standard}'/${coinType}'/${account}'/${change}/${addressIndex}`;
|
|
92
|
+
}
|
package/src/mnemonic/Mnemonic.ts
CHANGED
|
@@ -10,11 +10,15 @@ import * as ecc from '@bitcoinerlab/secp256k1';
|
|
|
10
10
|
import { initEccLib, Network, networks } from '@btc-vision/bitcoin';
|
|
11
11
|
import { Wallet } from '../keypair/Wallet.js';
|
|
12
12
|
import { MnemonicStrength } from './MnemonicStrength.js';
|
|
13
|
+
import { BIPStandard, buildBIPPath } from './BIPStandard.js';
|
|
14
|
+
import { AddressTypes } from '../keypair/AddressVerificator.js';
|
|
13
15
|
|
|
14
16
|
initEccLib(ecc);
|
|
15
17
|
|
|
16
18
|
const bip32 = BIP32Factory(ecc);
|
|
17
19
|
|
|
20
|
+
export { BIPStandard, getBIPDescription } from './BIPStandard.js';
|
|
21
|
+
|
|
18
22
|
/**
|
|
19
23
|
* Mnemonic class for managing BIP39 mnemonic phrases with BIP360 quantum support
|
|
20
24
|
*
|
|
@@ -189,7 +193,7 @@ export class Mnemonic {
|
|
|
189
193
|
}
|
|
190
194
|
|
|
191
195
|
/**
|
|
192
|
-
* Derive a wallet at a specific index using BIP360 (quantum) and
|
|
196
|
+
* Derive a wallet at a specific index using BIP360 (quantum) and configurable BIP standard (classical) paths
|
|
193
197
|
*
|
|
194
198
|
* This method derives both classical ECDSA/Schnorr keys and quantum-resistant ML-DSA keys
|
|
195
199
|
* for the wallet, providing hybrid post-quantum security.
|
|
@@ -197,11 +201,29 @@ export class Mnemonic {
|
|
|
197
201
|
* @param index - The address index to derive (default: 0)
|
|
198
202
|
* @param account - The account index (default: 0)
|
|
199
203
|
* @param isChange - Whether this is a change address (default: false)
|
|
204
|
+
* @param bipStandard - The BIP standard to use for classical derivation (default: BIP84)
|
|
200
205
|
* @returns A Wallet instance with both classical and quantum keys
|
|
206
|
+
*
|
|
207
|
+
* @example
|
|
208
|
+
* ```typescript
|
|
209
|
+
* // Default: BIP84 (Native SegWit)
|
|
210
|
+
* const wallet1 = mnemonic.derive(0);
|
|
211
|
+
*
|
|
212
|
+
* // BIP44 (Compatible with Unisat)
|
|
213
|
+
* const wallet2 = mnemonic.derive(0, 0, false, BIPStandard.BIP44);
|
|
214
|
+
*
|
|
215
|
+
* // BIP86 (Taproot)
|
|
216
|
+
* const wallet3 = mnemonic.derive(0, 0, false, BIPStandard.BIP86);
|
|
217
|
+
* ```
|
|
201
218
|
*/
|
|
202
|
-
public derive(
|
|
203
|
-
|
|
204
|
-
|
|
219
|
+
public derive(
|
|
220
|
+
index: number = 0,
|
|
221
|
+
account: number = 0,
|
|
222
|
+
isChange: boolean = false,
|
|
223
|
+
bipStandard: BIPStandard = BIPStandard.BIP84,
|
|
224
|
+
): Wallet {
|
|
225
|
+
// Derive classical key using specified BIP standard
|
|
226
|
+
const classicalPath = this.buildClassicalPath(account, index, isChange, bipStandard);
|
|
205
227
|
const classicalChild = this._classicalRoot.derivePath(classicalPath);
|
|
206
228
|
|
|
207
229
|
if (!classicalChild.privateKey) {
|
|
@@ -225,6 +247,101 @@ export class Mnemonic {
|
|
|
225
247
|
);
|
|
226
248
|
}
|
|
227
249
|
|
|
250
|
+
/**
|
|
251
|
+
* Derive a Unisat-compatible wallet
|
|
252
|
+
*
|
|
253
|
+
* Unisat uses different derivation paths based on address type:
|
|
254
|
+
* - Legacy (P2PKH): m/44'/coinType'/account'/change/index
|
|
255
|
+
* - Nested SegWit (P2SH-P2WPKH): m/49'/coinType'/account'/change/index
|
|
256
|
+
* - Native SegWit (P2WPKH): m/84'/coinType'/account'/change/index
|
|
257
|
+
* - Taproot (P2TR): m/86'/coinType'/account'/change/index
|
|
258
|
+
*
|
|
259
|
+
* @param addressType - The address type to generate
|
|
260
|
+
* @param index - The address index (default: 0)
|
|
261
|
+
* @param account - The account index (default: 0)
|
|
262
|
+
* @param isChange - Whether this is a change address (default: false)
|
|
263
|
+
* @returns A Wallet instance with both classical and quantum keys
|
|
264
|
+
*/
|
|
265
|
+
public deriveUnisat(
|
|
266
|
+
addressType: AddressTypes = AddressTypes.P2TR,
|
|
267
|
+
index: number = 0,
|
|
268
|
+
account: number = 0,
|
|
269
|
+
isChange: boolean = false,
|
|
270
|
+
): Wallet {
|
|
271
|
+
// Determine BIP purpose based on address type
|
|
272
|
+
let purpose: number;
|
|
273
|
+
switch (addressType) {
|
|
274
|
+
case AddressTypes.P2PKH:
|
|
275
|
+
purpose = 44;
|
|
276
|
+
break;
|
|
277
|
+
case AddressTypes.P2SH_OR_P2SH_P2WPKH:
|
|
278
|
+
purpose = 49;
|
|
279
|
+
break;
|
|
280
|
+
case AddressTypes.P2WPKH:
|
|
281
|
+
purpose = 84;
|
|
282
|
+
break;
|
|
283
|
+
case AddressTypes.P2TR:
|
|
284
|
+
purpose = 86;
|
|
285
|
+
break;
|
|
286
|
+
default:
|
|
287
|
+
throw new Error(`Unsupported address type: ${addressType}`);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// Build classical derivation path for Unisat
|
|
291
|
+
const coinType = this.getCoinType();
|
|
292
|
+
const change = isChange ? 1 : 0;
|
|
293
|
+
const classicalPath = `m/${purpose}'/0'/${account}'/${change}/${index}`;
|
|
294
|
+
|
|
295
|
+
// Derive classical key
|
|
296
|
+
const classicalChild = this._classicalRoot.derivePath(classicalPath);
|
|
297
|
+
|
|
298
|
+
if (!classicalChild.privateKey) {
|
|
299
|
+
throw new Error(`Failed to derive classical private key at path ${classicalPath}`);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// Derive quantum key using BIP360
|
|
303
|
+
const quantumPath = `m/360'/${coinType}'/${account}'/${change}/${index}`;
|
|
304
|
+
const quantumChild = this._quantumRoot.derivePath(quantumPath);
|
|
305
|
+
|
|
306
|
+
if (!quantumChild.privateKey) {
|
|
307
|
+
throw new Error(`Failed to derive quantum private key at path ${quantumPath}`);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// Create wallet with both classical and quantum keys
|
|
311
|
+
return new Wallet(
|
|
312
|
+
Buffer.from(classicalChild.privateKey).toString('hex'),
|
|
313
|
+
Buffer.from(quantumChild.privateKey).toString('hex'),
|
|
314
|
+
this._network,
|
|
315
|
+
this._securityLevel,
|
|
316
|
+
);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Derive multiple Unisat-compatible wallets
|
|
321
|
+
*
|
|
322
|
+
* @param addressType - The address type to generate
|
|
323
|
+
* @param count - Number of wallets to derive
|
|
324
|
+
* @param startIndex - Starting index (default: 0)
|
|
325
|
+
* @param account - The account index (default: 0)
|
|
326
|
+
* @param isChange - Whether these are change addresses (default: false)
|
|
327
|
+
* @returns Array of Wallet instances
|
|
328
|
+
*/
|
|
329
|
+
public deriveMultipleUnisat(
|
|
330
|
+
addressType: AddressTypes = AddressTypes.P2TR,
|
|
331
|
+
count: number = 5,
|
|
332
|
+
startIndex: number = 0,
|
|
333
|
+
account: number = 0,
|
|
334
|
+
isChange: boolean = false,
|
|
335
|
+
): Wallet[] {
|
|
336
|
+
const wallets: Wallet[] = [];
|
|
337
|
+
|
|
338
|
+
for (let i = 0; i < count; i++) {
|
|
339
|
+
wallets.push(this.deriveUnisat(addressType, startIndex + i, account, isChange));
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
return wallets;
|
|
343
|
+
}
|
|
344
|
+
|
|
228
345
|
/**
|
|
229
346
|
* Derive multiple wallets with sequential indices
|
|
230
347
|
*
|
|
@@ -232,6 +349,7 @@ export class Mnemonic {
|
|
|
232
349
|
* @param startIndex - The starting address index (default: 0)
|
|
233
350
|
* @param account - The account index (default: 0)
|
|
234
351
|
* @param isChange - Whether these are change addresses (default: false)
|
|
352
|
+
* @param bipStandard - The BIP standard to use for classical derivation (default: BIP84)
|
|
235
353
|
* @returns An array of Wallet instances
|
|
236
354
|
*/
|
|
237
355
|
public deriveMultiple(
|
|
@@ -239,11 +357,12 @@ export class Mnemonic {
|
|
|
239
357
|
startIndex: number = 0,
|
|
240
358
|
account: number = 0,
|
|
241
359
|
isChange: boolean = false,
|
|
360
|
+
bipStandard: BIPStandard = BIPStandard.BIP84,
|
|
242
361
|
): Wallet[] {
|
|
243
362
|
const wallets: Wallet[] = [];
|
|
244
363
|
|
|
245
364
|
for (let i = 0; i < count; i++) {
|
|
246
|
-
wallets.push(this.derive(startIndex + i, account, isChange));
|
|
365
|
+
wallets.push(this.derive(startIndex + i, account, isChange, bipStandard));
|
|
247
366
|
}
|
|
248
367
|
|
|
249
368
|
return wallets;
|
|
@@ -296,17 +415,23 @@ export class Mnemonic {
|
|
|
296
415
|
}
|
|
297
416
|
|
|
298
417
|
/**
|
|
299
|
-
* Build a classical derivation path
|
|
418
|
+
* Build a classical derivation path using specified BIP standard
|
|
300
419
|
*
|
|
301
420
|
* @param account - The account index
|
|
302
421
|
* @param index - The address index
|
|
303
422
|
* @param isChange - Whether this is a change address
|
|
423
|
+
* @param bipStandard - The BIP standard to use (default: BIP84)
|
|
304
424
|
* @returns The derivation path string
|
|
305
425
|
*/
|
|
306
|
-
private buildClassicalPath(
|
|
426
|
+
private buildClassicalPath(
|
|
427
|
+
account: number,
|
|
428
|
+
index: number,
|
|
429
|
+
isChange: boolean,
|
|
430
|
+
bipStandard: BIPStandard = BIPStandard.BIP84,
|
|
431
|
+
): string {
|
|
307
432
|
const coinType = this.getCoinType();
|
|
308
433
|
const change = isChange ? 1 : 0;
|
|
309
|
-
return
|
|
434
|
+
return buildBIPPath(bipStandard, coinType, account, change, index);
|
|
310
435
|
}
|
|
311
436
|
|
|
312
437
|
/**
|
package/src/opnet.ts
CHANGED