navio-sdk 0.1.0 → 0.1.2
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 +277 -13
- package/dist/index.d.mts +435 -2
- package/dist/index.d.ts +435 -2
- package/dist/index.js +908 -5
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +891 -6
- package/dist/index.mjs.map +1 -1
- package/package.json +9 -2
package/README.md
CHANGED
|
@@ -5,6 +5,7 @@ TypeScript SDK for interacting with the Navio blockchain. Provides wallet manage
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
7
|
- **Wallet Management** - HD key derivation with BLS CT support
|
|
8
|
+
- **Wallet Encryption** - Password-based encryption with Argon2id + AES-256-GCM
|
|
8
9
|
- **Dual Sync Backends** - Electrum protocol or direct P2P node connections
|
|
9
10
|
- **Automatic Output Detection** - BLSCT output scanning with view tag optimization
|
|
10
11
|
- **Amount Recovery** - Decrypt confidential transaction amounts
|
|
@@ -43,8 +44,8 @@ await client.initialize();
|
|
|
43
44
|
|
|
44
45
|
// Sync transaction keys
|
|
45
46
|
await client.sync({
|
|
46
|
-
onProgress: (height, tip, blocks, txKeys) => {
|
|
47
|
-
console.log(`Syncing: ${height}/${tip} (${blocks} blocks, ${txKeys} TX keys)`);
|
|
47
|
+
onProgress: (height, tip, blocks, txKeys, isReorg) => {
|
|
48
|
+
console.log(`Syncing: ${height}/${tip} (${blocks} blocks, ${txKeys} TX keys${isReorg ? ' [REORG]' : ''})`);
|
|
48
49
|
},
|
|
49
50
|
});
|
|
50
51
|
|
|
@@ -101,6 +102,7 @@ interface NavioClientConfig {
|
|
|
101
102
|
// Wallet options
|
|
102
103
|
createWalletIfNotExists?: boolean; // Create wallet if missing (default: false)
|
|
103
104
|
restoreFromSeed?: string; // Restore from seed (hex string)
|
|
105
|
+
restoreFromMnemonic?: string; // Restore from BIP39 mnemonic (12-24 words)
|
|
104
106
|
restoreFromHeight?: number; // Block height when wallet was created (for restore)
|
|
105
107
|
creationHeight?: number; // Creation height for new wallets (default: chainTip - 100)
|
|
106
108
|
}
|
|
@@ -167,6 +169,16 @@ Returns the last synced block height, or -1 if never synced.
|
|
|
167
169
|
|
|
168
170
|
Returns current sync state with statistics.
|
|
169
171
|
|
|
172
|
+
```typescript
|
|
173
|
+
interface SyncState {
|
|
174
|
+
lastSyncedHeight: number; // Last synced block height
|
|
175
|
+
lastSyncedHash: string; // Last synced block hash
|
|
176
|
+
totalTxKeysSynced: number; // Total transaction keys synced
|
|
177
|
+
lastSyncTime: number; // Last sync timestamp (ms)
|
|
178
|
+
chainTipAtLastSync: number; // Chain tip at last sync
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
170
182
|
#### Background Sync
|
|
171
183
|
|
|
172
184
|
##### `startBackgroundSync(options?: BackgroundSyncOptions): Promise<void>`
|
|
@@ -272,10 +284,6 @@ Check if client is connected to the backend.
|
|
|
272
284
|
|
|
273
285
|
#### Connection
|
|
274
286
|
|
|
275
|
-
##### `connect(): Promise<void>`
|
|
276
|
-
|
|
277
|
-
Connect to the backend.
|
|
278
|
-
|
|
279
287
|
##### `disconnect(): Promise<void>`
|
|
280
288
|
|
|
281
289
|
Disconnect from backend and close database.
|
|
@@ -300,12 +308,48 @@ const masterSeed = keyManager.getMasterSeedKey();
|
|
|
300
308
|
console.log('Seed:', masterSeed.serialize()); // hex string
|
|
301
309
|
```
|
|
302
310
|
|
|
311
|
+
#### Mnemonic Support (BIP39)
|
|
312
|
+
|
|
313
|
+
```typescript
|
|
314
|
+
// Generate a new 24-word mnemonic and set as seed
|
|
315
|
+
const mnemonic = keyManager.generateNewMnemonic();
|
|
316
|
+
console.log('Mnemonic:', mnemonic);
|
|
317
|
+
// "abandon ability able about above absent absorb abstract absurd abuse access accident..."
|
|
318
|
+
|
|
319
|
+
// Or generate mnemonic with different word counts
|
|
320
|
+
const mnemonic12 = KeyManager.generateMnemonic(128); // 12 words
|
|
321
|
+
const mnemonic24 = KeyManager.generateMnemonic(256); // 24 words (default)
|
|
322
|
+
|
|
323
|
+
// Validate a mnemonic
|
|
324
|
+
const isValid = KeyManager.validateMnemonic(mnemonic);
|
|
325
|
+
console.log('Valid:', isValid); // true
|
|
326
|
+
|
|
327
|
+
// Restore from mnemonic
|
|
328
|
+
keyManager.setHDSeedFromMnemonic(mnemonic);
|
|
329
|
+
|
|
330
|
+
// Get mnemonic from current seed (for backup)
|
|
331
|
+
const backupMnemonic = keyManager.getMnemonic();
|
|
332
|
+
console.log('Backup mnemonic:', backupMnemonic);
|
|
333
|
+
|
|
334
|
+
// Static conversion methods
|
|
335
|
+
const scalar = KeyManager.mnemonicToScalar(mnemonic); // Convert to Scalar
|
|
336
|
+
const recovered = KeyManager.seedToMnemonic(scalar); // Convert back to mnemonic
|
|
337
|
+
```
|
|
338
|
+
|
|
303
339
|
#### Sub-addresses
|
|
304
340
|
|
|
305
341
|
```typescript
|
|
306
342
|
// Get sub-address by identifier
|
|
307
343
|
const subAddr = keyManager.getSubAddress({ account: 0, address: 0 });
|
|
308
344
|
|
|
345
|
+
// Get bech32m encoded address string (recommended)
|
|
346
|
+
const address = keyManager.getSubAddressBech32m({ account: 0, address: 0 }, 'testnet');
|
|
347
|
+
console.log('Address:', address); // tnav1... (Node.js) or hex fallback (browser)
|
|
348
|
+
|
|
349
|
+
// Get mainnet address
|
|
350
|
+
const mainnetAddress = keyManager.getSubAddressBech32m({ account: 0, address: 0 }, 'mainnet');
|
|
351
|
+
console.log('Address:', mainnetAddress); // nav1...
|
|
352
|
+
|
|
309
353
|
// Generate new sub-address
|
|
310
354
|
const { subAddress, id } = keyManager.generateNewSubAddress(0); // account 0
|
|
311
355
|
|
|
@@ -315,6 +359,9 @@ keyManager.newSubAddressPool(-1); // Change
|
|
|
315
359
|
keyManager.newSubAddressPool(-2); // Staking
|
|
316
360
|
```
|
|
317
361
|
|
|
362
|
+
> **Note:** In browser environments using navio-blsct WASM, bech32m encoding may fall back to hex
|
|
363
|
+
> representation due to a known issue. This will be addressed in a future navio-blsct release.
|
|
364
|
+
|
|
318
365
|
#### Output Detection
|
|
319
366
|
|
|
320
367
|
```typescript
|
|
@@ -331,6 +378,38 @@ const hashId = keyManager.calculateHashId(blindingKey, spendingKey);
|
|
|
331
378
|
const nonce = keyManager.calculateNonce(blindingKey);
|
|
332
379
|
```
|
|
333
380
|
|
|
381
|
+
#### Wallet Encryption
|
|
382
|
+
|
|
383
|
+
The KeyManager supports password-based encryption using Argon2id for key derivation and AES-256-GCM for encryption.
|
|
384
|
+
|
|
385
|
+
```typescript
|
|
386
|
+
// Check encryption status
|
|
387
|
+
const isEncrypted = keyManager.isEncrypted();
|
|
388
|
+
const isUnlocked = keyManager.isUnlocked();
|
|
389
|
+
|
|
390
|
+
// Set a password (encrypts the wallet)
|
|
391
|
+
await keyManager.setPassword('my-secure-password');
|
|
392
|
+
|
|
393
|
+
// Lock the wallet (clears cached encryption key)
|
|
394
|
+
keyManager.lock();
|
|
395
|
+
|
|
396
|
+
// Unlock the wallet
|
|
397
|
+
const success = await keyManager.unlock('my-secure-password');
|
|
398
|
+
if (!success) {
|
|
399
|
+
console.log('Wrong password');
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// Change password
|
|
403
|
+
const changed = await keyManager.changePassword('old-password', 'new-password');
|
|
404
|
+
|
|
405
|
+
// Get encryption parameters (for database storage)
|
|
406
|
+
const params = keyManager.getEncryptionParams();
|
|
407
|
+
// { salt: 'hex...', verificationHash: 'hex...' }
|
|
408
|
+
|
|
409
|
+
// Restore encryption state from database
|
|
410
|
+
keyManager.setEncryptionParams(params.salt, params.verificationHash);
|
|
411
|
+
```
|
|
412
|
+
|
|
334
413
|
---
|
|
335
414
|
|
|
336
415
|
### WalletDB
|
|
@@ -381,6 +460,50 @@ const utxos = await walletDB.getUnspentOutputs();
|
|
|
381
460
|
const outputs = await walletDB.getAllOutputs();
|
|
382
461
|
```
|
|
383
462
|
|
|
463
|
+
#### Database Encryption
|
|
464
|
+
|
|
465
|
+
The WalletDB supports full database encryption for secure backup and export.
|
|
466
|
+
|
|
467
|
+
```typescript
|
|
468
|
+
// Export database as encrypted blob
|
|
469
|
+
const encryptedData = await walletDB.exportEncrypted('backup-password');
|
|
470
|
+
// encryptedData is a Uint8Array that can be saved to file
|
|
471
|
+
|
|
472
|
+
// Load database from encrypted blob
|
|
473
|
+
const restoredDb = await WalletDB.loadEncrypted(encryptedData, 'backup-password');
|
|
474
|
+
|
|
475
|
+
// Check if a buffer is an encrypted database
|
|
476
|
+
import { isEncryptedDatabase } from 'navio-sdk';
|
|
477
|
+
const isEncrypted = isEncryptedDatabase(someBuffer);
|
|
478
|
+
|
|
479
|
+
// Export unencrypted database (for backup without password)
|
|
480
|
+
const rawData = walletDB.export();
|
|
481
|
+
|
|
482
|
+
// Load from raw bytes
|
|
483
|
+
const db = await WalletDB.loadFromBytes(rawData);
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
#### Encryption Metadata
|
|
487
|
+
|
|
488
|
+
```typescript
|
|
489
|
+
// Check if encryption is enabled
|
|
490
|
+
const isEncrypted = walletDB.isEncrypted();
|
|
491
|
+
|
|
492
|
+
// Save encryption metadata
|
|
493
|
+
walletDB.saveEncryptionMetadata(salt, verificationHash);
|
|
494
|
+
|
|
495
|
+
// Get encryption metadata
|
|
496
|
+
const metadata = walletDB.getEncryptionMetadata();
|
|
497
|
+
// { salt: 'hex...', verificationHash: 'hex...' } or null
|
|
498
|
+
|
|
499
|
+
// Get/save encrypted keys
|
|
500
|
+
walletDB.saveEncryptedKey(keyId, publicKey, encryptedSecret);
|
|
501
|
+
const key = walletDB.getEncryptedKey(keyId);
|
|
502
|
+
|
|
503
|
+
// Delete plaintext keys after encryption
|
|
504
|
+
walletDB.deletePlaintextKeys();
|
|
505
|
+
```
|
|
506
|
+
|
|
384
507
|
---
|
|
385
508
|
|
|
386
509
|
### SyncProvider Interface
|
|
@@ -396,6 +519,7 @@ interface SyncProvider {
|
|
|
396
519
|
isConnected(): boolean;
|
|
397
520
|
|
|
398
521
|
getChainTipHeight(): Promise<number>;
|
|
522
|
+
getChainTip(): Promise<{ height: number; hash: string }>;
|
|
399
523
|
getBlockHeader(height: number): Promise<string>;
|
|
400
524
|
getBlockHeaders(startHeight: number, count: number): Promise<HeadersResult>;
|
|
401
525
|
getBlockTransactionKeysRange(startHeight: number): Promise<TransactionKeysRangeResult>;
|
|
@@ -436,7 +560,7 @@ import { P2PSyncProvider } from 'navio-sdk';
|
|
|
436
560
|
|
|
437
561
|
const provider = new P2PSyncProvider({
|
|
438
562
|
host: 'localhost',
|
|
439
|
-
port: 33670
|
|
563
|
+
port: 43670, // testnet port (mainnet: 33670)
|
|
440
564
|
network: 'testnet',
|
|
441
565
|
debug: true,
|
|
442
566
|
});
|
|
@@ -468,12 +592,9 @@ const keyManager = client.getKeyManager();
|
|
|
468
592
|
const seed = keyManager.getMasterSeedKey();
|
|
469
593
|
console.log('SAVE THIS SEED:', seed.serialize());
|
|
470
594
|
|
|
471
|
-
// Get receiving address
|
|
472
|
-
const {
|
|
473
|
-
|
|
474
|
-
const dpk = DoublePublicKey.deserialize(subAddress.serialize());
|
|
475
|
-
const address = Address.encode(dpk, AddressEncoding.Bech32M);
|
|
476
|
-
console.log('Address:', address);
|
|
595
|
+
// Get receiving address (bech32m encoded)
|
|
596
|
+
const address = keyManager.getSubAddressBech32m({ account: 0, address: 0 }, 'testnet');
|
|
597
|
+
console.log('Address:', address); // tnav1...
|
|
477
598
|
```
|
|
478
599
|
|
|
479
600
|
### Restore Wallet from Seed
|
|
@@ -497,6 +618,27 @@ await client.sync({
|
|
|
497
618
|
});
|
|
498
619
|
```
|
|
499
620
|
|
|
621
|
+
### Restore Wallet from Mnemonic
|
|
622
|
+
|
|
623
|
+
```typescript
|
|
624
|
+
const client = new NavioClient({
|
|
625
|
+
walletDbPath: './restored-wallet.db',
|
|
626
|
+
electrum: { host: 'localhost', port: 50005 },
|
|
627
|
+
restoreFromMnemonic: 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art',
|
|
628
|
+
restoreFromHeight: 50000, // Block height when wallet was created
|
|
629
|
+
network: 'testnet',
|
|
630
|
+
});
|
|
631
|
+
|
|
632
|
+
await client.initialize();
|
|
633
|
+
|
|
634
|
+
// Full sync from restoration height
|
|
635
|
+
await client.sync({
|
|
636
|
+
onProgress: (height, tip) => {
|
|
637
|
+
console.log(`Syncing: ${height}/${tip}`);
|
|
638
|
+
},
|
|
639
|
+
});
|
|
640
|
+
```
|
|
641
|
+
|
|
500
642
|
### Using P2P Backend
|
|
501
643
|
|
|
502
644
|
```typescript
|
|
@@ -607,6 +749,77 @@ for (const utxo of utxos) {
|
|
|
607
749
|
}
|
|
608
750
|
```
|
|
609
751
|
|
|
752
|
+
### Encrypt Wallet with Password
|
|
753
|
+
|
|
754
|
+
```typescript
|
|
755
|
+
import { NavioClient } from 'navio-sdk';
|
|
756
|
+
|
|
757
|
+
const client = new NavioClient({
|
|
758
|
+
walletDbPath: './secure-wallet.db',
|
|
759
|
+
electrum: { host: 'localhost', port: 50005 },
|
|
760
|
+
createWalletIfNotExists: true,
|
|
761
|
+
network: 'testnet',
|
|
762
|
+
});
|
|
763
|
+
|
|
764
|
+
await client.initialize();
|
|
765
|
+
|
|
766
|
+
// Set a password to encrypt the wallet
|
|
767
|
+
const keyManager = client.getKeyManager();
|
|
768
|
+
await keyManager.setPassword('my-secure-password');
|
|
769
|
+
console.log('Wallet encrypted:', keyManager.isEncrypted()); // true
|
|
770
|
+
|
|
771
|
+
// The wallet is now encrypted but unlocked
|
|
772
|
+
console.log('Wallet unlocked:', keyManager.isUnlocked()); // true
|
|
773
|
+
|
|
774
|
+
// Lock the wallet (recommended when not in use)
|
|
775
|
+
keyManager.lock();
|
|
776
|
+
console.log('Wallet unlocked:', keyManager.isUnlocked()); // false
|
|
777
|
+
|
|
778
|
+
// Unlock to use
|
|
779
|
+
const success = await keyManager.unlock('my-secure-password');
|
|
780
|
+
console.log('Unlock successful:', success); // true
|
|
781
|
+
```
|
|
782
|
+
|
|
783
|
+
### Export Encrypted Backup
|
|
784
|
+
|
|
785
|
+
```typescript
|
|
786
|
+
// Export encrypted database for backup
|
|
787
|
+
const walletDb = client.getWalletDB();
|
|
788
|
+
const encryptedBackup = await walletDb.exportEncrypted('backup-password');
|
|
789
|
+
|
|
790
|
+
// Save to file (Node.js)
|
|
791
|
+
const fs = require('fs');
|
|
792
|
+
fs.writeFileSync('wallet-backup.enc', encryptedBackup);
|
|
793
|
+
|
|
794
|
+
// Or download in browser
|
|
795
|
+
const blob = new Blob([encryptedBackup], { type: 'application/octet-stream' });
|
|
796
|
+
const url = URL.createObjectURL(blob);
|
|
797
|
+
const a = document.createElement('a');
|
|
798
|
+
a.href = url;
|
|
799
|
+
a.download = 'wallet-backup.enc';
|
|
800
|
+
a.click();
|
|
801
|
+
```
|
|
802
|
+
|
|
803
|
+
### Restore from Encrypted Backup
|
|
804
|
+
|
|
805
|
+
```typescript
|
|
806
|
+
import { WalletDB } from 'navio-sdk';
|
|
807
|
+
|
|
808
|
+
// Load encrypted backup
|
|
809
|
+
const fs = require('fs');
|
|
810
|
+
const encryptedData = fs.readFileSync('wallet-backup.enc');
|
|
811
|
+
|
|
812
|
+
// Decrypt and load
|
|
813
|
+
const walletDb = await WalletDB.loadEncrypted(
|
|
814
|
+
new Uint8Array(encryptedData),
|
|
815
|
+
'backup-password'
|
|
816
|
+
);
|
|
817
|
+
|
|
818
|
+
// Load the wallet
|
|
819
|
+
const keyManager = await walletDb.loadWallet();
|
|
820
|
+
console.log('Wallet restored');
|
|
821
|
+
```
|
|
822
|
+
|
|
610
823
|
---
|
|
611
824
|
|
|
612
825
|
## Database Schema
|
|
@@ -617,12 +830,15 @@ The wallet database includes the following tables:
|
|
|
617
830
|
|-------|-------------|
|
|
618
831
|
| `keys` | Key pairs for transactions |
|
|
619
832
|
| `out_keys` | Output-specific keys |
|
|
833
|
+
| `crypted_keys` | Encrypted key pairs (when password set) |
|
|
834
|
+
| `crypted_out_keys` | Encrypted output keys (when password set) |
|
|
620
835
|
| `view_key` | View key for output detection |
|
|
621
836
|
| `spend_key` | Spending public key |
|
|
622
837
|
| `hd_chain` | HD chain information |
|
|
623
838
|
| `sub_addresses` | Sub-address mappings |
|
|
624
839
|
| `wallet_outputs` | Wallet UTXOs with amounts |
|
|
625
840
|
| `wallet_metadata` | Wallet creation info |
|
|
841
|
+
| `encryption_metadata` | Encryption parameters (salt, verification hash) |
|
|
626
842
|
| `tx_keys` | Transaction keys (optional) |
|
|
627
843
|
| `block_hashes` | Block hashes for reorg detection |
|
|
628
844
|
| `sync_state` | Synchronization state |
|
|
@@ -687,12 +903,14 @@ npm install
|
|
|
687
903
|
npm run build
|
|
688
904
|
|
|
689
905
|
# Run tests
|
|
906
|
+
npm run test # Run all unit tests (vitest)
|
|
690
907
|
npm run test:keymanager # KeyManager tests
|
|
691
908
|
npm run test:walletdb # WalletDB tests
|
|
692
909
|
npm run test:electrum # Electrum client tests
|
|
693
910
|
npm run test:client # Full client tests (Electrum)
|
|
694
911
|
npm run test:p2p # P2P protocol tests
|
|
695
912
|
npm run test:client:p2p # Full client tests (P2P)
|
|
913
|
+
npm run test:encryption # Encryption module tests
|
|
696
914
|
|
|
697
915
|
# Generate documentation
|
|
698
916
|
npm run docs # HTML docs in ./docs
|
|
@@ -711,6 +929,52 @@ npm run analyze:db
|
|
|
711
929
|
|
|
712
930
|
---
|
|
713
931
|
|
|
932
|
+
### Encryption Module
|
|
933
|
+
|
|
934
|
+
The SDK includes a low-level encryption module for password-based encryption.
|
|
935
|
+
|
|
936
|
+
```typescript
|
|
937
|
+
import {
|
|
938
|
+
encrypt,
|
|
939
|
+
decrypt,
|
|
940
|
+
deriveKey,
|
|
941
|
+
serializeEncryptedData,
|
|
942
|
+
deserializeEncryptedData,
|
|
943
|
+
createPasswordVerification,
|
|
944
|
+
verifyPassword,
|
|
945
|
+
randomBytes,
|
|
946
|
+
} from 'navio-sdk';
|
|
947
|
+
|
|
948
|
+
// Encrypt arbitrary data
|
|
949
|
+
const plaintext = new TextEncoder().encode('secret data');
|
|
950
|
+
const encrypted = await encrypt(plaintext, 'password');
|
|
951
|
+
|
|
952
|
+
// Decrypt data
|
|
953
|
+
const decrypted = await decrypt(encrypted, 'password');
|
|
954
|
+
console.log(new TextDecoder().decode(decrypted)); // 'secret data'
|
|
955
|
+
|
|
956
|
+
// Serialize for storage (converts to base64 strings)
|
|
957
|
+
const serialized = serializeEncryptedData(encrypted);
|
|
958
|
+
const json = JSON.stringify(serialized);
|
|
959
|
+
|
|
960
|
+
// Deserialize from storage
|
|
961
|
+
const parsed = JSON.parse(json);
|
|
962
|
+
const deserialized = deserializeEncryptedData(parsed);
|
|
963
|
+
|
|
964
|
+
// Password verification (for UI feedback)
|
|
965
|
+
const salt = randomBytes(16);
|
|
966
|
+
const hash = await createPasswordVerification('password', salt);
|
|
967
|
+
const isValid = await verifyPassword('password', salt, hash);
|
|
968
|
+
```
|
|
969
|
+
|
|
970
|
+
**Security Properties:**
|
|
971
|
+
- **Key Derivation**: Argon2id with 64MB memory, 3 iterations, 4 parallelism
|
|
972
|
+
- **Encryption**: AES-256-GCM (authenticated encryption)
|
|
973
|
+
- **Random IV**: 12 bytes per encryption (never reused)
|
|
974
|
+
- **Random Salt**: 16 bytes per password derivation
|
|
975
|
+
|
|
976
|
+
---
|
|
977
|
+
|
|
714
978
|
## Known Limitations
|
|
715
979
|
|
|
716
980
|
1. **Amount Recovery**: The `navio-blsct` library v1.0.20 has a bug in `RangeProof.recoverAmounts()`. Outputs are detected and stored but amounts show as 0 until the library is updated.
|