@bsv/sdk 1.9.2 → 1.9.4

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.
Files changed (93) hide show
  1. package/dist/cjs/package.json +1 -1
  2. package/dist/cjs/src/kvstore/GlobalKVStore.js +116 -98
  3. package/dist/cjs/src/kvstore/GlobalKVStore.js.map +1 -1
  4. package/dist/cjs/src/kvstore/types.js.map +1 -1
  5. package/dist/cjs/src/overlay-tools/index.js +1 -0
  6. package/dist/cjs/src/overlay-tools/index.js.map +1 -1
  7. package/dist/cjs/src/overlay-tools/withDoubleSpendRetry.js +55 -0
  8. package/dist/cjs/src/overlay-tools/withDoubleSpendRetry.js.map +1 -0
  9. package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
  10. package/dist/esm/src/kvstore/GlobalKVStore.js +117 -99
  11. package/dist/esm/src/kvstore/GlobalKVStore.js.map +1 -1
  12. package/dist/esm/src/kvstore/types.js.map +1 -1
  13. package/dist/esm/src/overlay-tools/index.js +1 -0
  14. package/dist/esm/src/overlay-tools/index.js.map +1 -1
  15. package/dist/esm/src/overlay-tools/withDoubleSpendRetry.js +48 -0
  16. package/dist/esm/src/overlay-tools/withDoubleSpendRetry.js.map +1 -0
  17. package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
  18. package/dist/types/src/kvstore/GlobalKVStore.d.ts.map +1 -1
  19. package/dist/types/src/kvstore/types.d.ts +2 -0
  20. package/dist/types/src/kvstore/types.d.ts.map +1 -1
  21. package/dist/types/src/overlay-tools/index.d.ts +1 -0
  22. package/dist/types/src/overlay-tools/index.d.ts.map +1 -1
  23. package/dist/types/src/overlay-tools/withDoubleSpendRetry.d.ts +14 -0
  24. package/dist/types/src/overlay-tools/withDoubleSpendRetry.d.ts.map +1 -0
  25. package/dist/types/tsconfig.types.tsbuildinfo +1 -1
  26. package/dist/umd/bundle.js +2 -2
  27. package/dist/umd/bundle.js.map +1 -1
  28. package/docs/fast-docs.png +0 -0
  29. package/docs/index.md +49 -44
  30. package/docs/reference/kvstore.md +9 -0
  31. package/docs/reference/overlay-tools.md +32 -0
  32. package/docs/swagger.png +0 -0
  33. package/package.json +1 -1
  34. package/src/kvstore/GlobalKVStore.ts +134 -114
  35. package/src/kvstore/__tests/GlobalKVStore.test.ts +11 -1
  36. package/src/kvstore/types.ts +2 -0
  37. package/src/overlay-tools/index.ts +1 -0
  38. package/src/overlay-tools/withDoubleSpendRetry.ts +71 -0
  39. package/docs/MARKDOWN_VALIDATION_GUIDE.md +0 -175
  40. package/docs/concepts/beef.md +0 -92
  41. package/docs/concepts/chain-tracking.md +0 -134
  42. package/docs/concepts/decentralized-identity.md +0 -221
  43. package/docs/concepts/fees.md +0 -249
  44. package/docs/concepts/identity-certificates.md +0 -307
  45. package/docs/concepts/index.md +0 -77
  46. package/docs/concepts/key-management.md +0 -185
  47. package/docs/concepts/script-templates.md +0 -176
  48. package/docs/concepts/sdk-philosophy.md +0 -80
  49. package/docs/concepts/signatures.md +0 -194
  50. package/docs/concepts/spv-verification.md +0 -118
  51. package/docs/concepts/transaction-encoding.md +0 -167
  52. package/docs/concepts/transaction-structure.md +0 -67
  53. package/docs/concepts/trust-model.md +0 -139
  54. package/docs/concepts/verification.md +0 -250
  55. package/docs/concepts/wallet-integration.md +0 -101
  56. package/docs/guides/development-wallet-setup.md +0 -374
  57. package/docs/guides/direct-transaction-creation.md +0 -147
  58. package/docs/guides/http-client-configuration.md +0 -488
  59. package/docs/guides/index.md +0 -138
  60. package/docs/guides/large-transactions.md +0 -448
  61. package/docs/guides/multisig-transactions.md +0 -792
  62. package/docs/guides/security-best-practices.md +0 -494
  63. package/docs/guides/transaction-batching.md +0 -132
  64. package/docs/guides/transaction-signing-methods.md +0 -419
  65. package/docs/reference/arc-config.md +0 -698
  66. package/docs/reference/brc-100.md +0 -33
  67. package/docs/reference/configuration.md +0 -835
  68. package/docs/reference/debugging.md +0 -705
  69. package/docs/reference/errors.md +0 -597
  70. package/docs/reference/index.md +0 -111
  71. package/docs/reference/network-config.md +0 -914
  72. package/docs/reference/op-codes.md +0 -325
  73. package/docs/reference/transaction-signatures.md +0 -95
  74. package/docs/tutorials/advanced-transaction.md +0 -572
  75. package/docs/tutorials/aes-encryption.md +0 -949
  76. package/docs/tutorials/authfetch-tutorial.md +0 -986
  77. package/docs/tutorials/ecdh-key-exchange.md +0 -549
  78. package/docs/tutorials/elliptic-curve-fundamentals.md +0 -606
  79. package/docs/tutorials/error-handling.md +0 -1216
  80. package/docs/tutorials/first-transaction-low-level.md +0 -205
  81. package/docs/tutorials/first-transaction.md +0 -275
  82. package/docs/tutorials/hashes-and-hmacs.md +0 -788
  83. package/docs/tutorials/identity-management.md +0 -729
  84. package/docs/tutorials/index.md +0 -219
  85. package/docs/tutorials/key-management.md +0 -538
  86. package/docs/tutorials/protowallet-development.md +0 -743
  87. package/docs/tutorials/script-construction.md +0 -690
  88. package/docs/tutorials/spv-merkle-proofs.md +0 -685
  89. package/docs/tutorials/testnet-transactions-low-level.md +0 -359
  90. package/docs/tutorials/transaction-broadcasting.md +0 -538
  91. package/docs/tutorials/transaction-types.md +0 -420
  92. package/docs/tutorials/type-42.md +0 -568
  93. package/docs/tutorials/uhrp-storage.md +0 -599
@@ -1,986 +0,0 @@
1
- # Authenticated HTTP Requests with AuthFetch
2
-
3
- **Duration**: 60 minutes
4
- **Prerequisites**: Node.js, basic TypeScript knowledge, understanding of HTTP and authentication
5
- **Learning Goals**:
6
-
7
- - Understand BRC-103/104 authentication protocols
8
- - Implement authenticated HTTP requests with AuthFetch
9
- - Handle peer-to-peer authentication and certificate exchange
10
- - Build secure API communication systems
11
-
12
- ## When to Use AuthFetch
13
-
14
- **Use AuthFetch when you need:**
15
-
16
- - BRC-103/104 cryptographic authentication
17
- - Wallet-signed HTTP requests for identity verification
18
- - Certificate-based peer authentication
19
- - Secure peer-to-peer communication between BSV applications
20
- - APIs that require cryptographic proof of identity
21
-
22
- **For general HTTP client configuration, use [HTTP Client Configuration Guide](../guides/http-client-configuration.md) instead:**
23
-
24
- - Custom HTTP client setup (Axios, fetch, etc.)
25
- - Transaction broadcasting via ARC endpoints
26
- - Environment-specific configuration (timeouts, retries)
27
- - Testing and mocking HTTP clients
28
- - Integration with existing HTTP infrastructure
29
-
30
- ## Introduction
31
-
32
- AuthFetch is a specialized HTTP client that implements BRC-103 and BRC-104 authentication protocols for secure peer-to-peer communication in the BSV ecosystem. Unlike traditional API authentication (like JWT tokens), AuthFetch uses cryptographic signatures and certificate-based authentication.
33
-
34
- ## Key Features
35
-
36
- - **BRC-103 Authentication**: Cryptographic request signing
37
- - **BRC-104 Certificate Exchange**: Peer identity verification
38
- - **Automatic Session Management**: Handles authentication state
39
- - **Certificate Validation**: Verifies peer credentials
40
- - **Secure Communication**: End-to-end authenticated requests
41
-
42
- ## What You'll Build
43
-
44
- In this tutorial, you'll create:
45
-
46
- - Basic authenticated HTTP client
47
- - Peer-to-peer communication system
48
- - Certificate exchange mechanism
49
- - Secure API integration
50
-
51
- ## Setting Up AuthFetch with `WalletClient`
52
-
53
- ### Basic AuthFetch Configuration
54
-
55
- ```typescript
56
- import { AuthFetch, WalletClient } from '@bsv/sdk'
57
-
58
- async function createAuthFetch() {
59
- // Create wallet for authentication - connects to local wallet (e.g., MetaNet Desktop)
60
- const wallet = new WalletClient('auto', 'localhost')
61
-
62
- // Check if wallet is connected
63
- try {
64
- const authStatus = await wallet.isAuthenticated()
65
- console.log('Wallet authenticated:', authStatus.authenticated)
66
-
67
- const network = await wallet.getNetwork()
68
- console.log('Connected to network:', network.network)
69
- } catch (error) {
70
- console.log('Wallet connection status:', error.message)
71
- // This is expected if no wallet is running
72
- }
73
-
74
- // Create AuthFetch instance
75
- const authFetch = new AuthFetch(wallet)
76
-
77
- console.log('AuthFetch client created')
78
- return authFetch
79
- }
80
-
81
- async function basicAuthenticatedRequest() {
82
- const authFetch = await createAuthFetch()
83
-
84
- try {
85
- // Make authenticated request to a real, working endpoint
86
- const response = await authFetch.fetch('https://httpbin.org/get', {
87
- method: 'GET',
88
- headers: {
89
- 'Content-Type': 'application/json',
90
- 'X-BSV-Tutorial': 'AuthFetch-Example'
91
- }
92
- })
93
-
94
- if (response.ok) {
95
- const data = await response.json()
96
- console.log('Authenticated request successful!')
97
- console.log('Request URL:', data.url)
98
- console.log('Headers sent:', Object.keys(data.headers).length)
99
- return data
100
- } else {
101
- console.error('Request failed:', response.status, response.statusText)
102
- }
103
- } catch (error) {
104
- console.error('Authentication error:', error.message)
105
- if (error.message.includes('No wallet available')) {
106
- console.log(' Install and run MetaNet Desktop Wallet to test with real authentication')
107
- console.log(' For now, this demonstrates the AuthFetch API structure')
108
- }
109
- }
110
- }
111
-
112
- // Test the basic functionality
113
- basicAuthenticatedRequest().catch(console.error)
114
- ```
115
-
116
- ### AuthFetch with Certificate Requirements
117
-
118
- ```typescript
119
- import { AuthFetch, WalletClient } from '@bsv/sdk'
120
-
121
- async function createAuthFetchWithCertificates() {
122
- const wallet = new WalletClient('auto', 'localhost')
123
-
124
- // Define required certificates from peers
125
- const requestedCertificates = {
126
- certifiers: {
127
- // Require identity certificates from trusted certifier
128
- 'identity-certifier-key': {
129
- certificateTypes: ['identity-cert'],
130
- fieldsRequired: ['name', 'email']
131
- }
132
- },
133
- acquisitionProtocol: 'direct' as const
134
- }
135
-
136
- const authFetch = new AuthFetch(wallet, requestedCertificates)
137
-
138
- console.log('AuthFetch with certificate requirements created')
139
- return authFetch
140
- }
141
-
142
- async function testCertificateRequirements() {
143
- const authFetch = await createAuthFetchWithCertificates()
144
-
145
- try {
146
- // Test with a real endpoint that will show our certificate headers (using a dummy URL for demo purposes)
147
- const response = await authFetch.fetch('https://httpbin.org/headers', {
148
- method: 'GET'
149
- })
150
-
151
- if (response.ok) {
152
- const data = await response.json()
153
- console.log('Certificate-enabled request successful!')
154
- console.log('Headers sent to server:', data.headers)
155
-
156
- // AuthFetch will include certificate-related headers when available
157
- const certHeaders = Object.keys(data.headers).filter(h =>
158
- h.toLowerCase().includes('cert') || h.toLowerCase().includes('auth')
159
- )
160
- console.log('Certificate/Auth headers:', certHeaders)
161
-
162
- } else {
163
- console.error('Request failed:', response.status)
164
- }
165
- } catch (error) {
166
- console.error('Certificate request error:', error.message)
167
- if (error.message.includes('No wallet available')) {
168
- console.log(' Certificate exchange requires a connected wallet')
169
- }
170
- }
171
- }
172
-
173
- testCertificateRequirements().catch(console.error)
174
- ```
175
-
176
- ## Certificate Exchange and Verification
177
-
178
- ### Requesting Certificates from Peers
179
-
180
- ```typescript
181
- import { AuthFetch, WalletClient } from '@bsv/sdk'
182
-
183
- class CertificateManager {
184
- private authFetch: AuthFetch
185
-
186
- constructor(wallet: WalletClient) {
187
- this.authFetch = new AuthFetch(wallet)
188
- }
189
-
190
- async requestPeerCertificates(
191
- peerBaseUrl: string,
192
- certificateRequirements: any
193
- ): Promise<any[]> {
194
- try {
195
- console.log('Requesting certificates from peer:', peerBaseUrl)
196
-
197
- const certificates = await this.authFetch.sendCertificateRequest(
198
- peerBaseUrl,
199
- certificateRequirements
200
- )
201
-
202
- console.log('Received certificates:', certificates.length)
203
- return certificates
204
- } catch (error) {
205
- console.error('Certificate request failed:', error)
206
- throw error
207
- }
208
- }
209
-
210
- async verifyPeerIdentity(peerUrl: string): Promise<{
211
- verified: boolean
212
- identity: string | null
213
- certificates: any[]
214
- }> {
215
- const certificateRequirements = {
216
- certifiers: {
217
- 'trusted-identity-provider': {
218
- certificateTypes: ['identity'],
219
- fieldsRequired: ['name']
220
- }
221
- },
222
- acquisitionProtocol: 'direct' as const
223
- }
224
-
225
- try {
226
- const certificates = await this.requestPeerCertificates(
227
- peerUrl,
228
- certificateRequirements
229
- )
230
-
231
- // Verify certificates (simplified verification)
232
- const verified = certificates.length > 0
233
- const identity = verified ? certificates[0].subject : null
234
-
235
- return { verified, identity, certificates }
236
- } catch (error) {
237
- console.error('Identity verification failed:', error)
238
- return { verified: false, identity: null, certificates: [] }
239
- }
240
- }
241
- }
242
-
243
- async function demonstrateCertificateExchange() {
244
- const wallet = new WalletClient('auto', 'localhost')
245
- const certManager = new CertificateManager(wallet)
246
-
247
- // Example peer URLs (replace with actual peer endpoints)
248
- const peerUrls = [
249
- 'https://peer1.example.com',
250
- 'https://peer2.example.com'
251
- ]
252
-
253
- for (const peerUrl of peerUrls) {
254
- console.log(`\n=== Verifying peer: ${peerUrl} ===`)
255
-
256
- try {
257
- const verification = await certManager.verifyPeerIdentity(peerUrl)
258
-
259
- if (verification.verified) {
260
- console.log(' Peer verified successfully')
261
- console.log('Identity:', verification.identity)
262
- console.log('Certificates received:', verification.certificates.length)
263
- } else {
264
- console.log(' Peer verification failed')
265
- }
266
- } catch (error) {
267
- console.log(' Peer unreachable or invalid')
268
- }
269
- }
270
- }
271
-
272
- demonstrateCertificateExchange().catch(console.error)
273
- ```
274
-
275
- ## Building Secure API Clients
276
-
277
- ### Authenticated API Client
278
-
279
- ```typescript
280
- import { AuthFetch, WalletClient } from '@bsv/sdk'
281
-
282
- class SecureAPIClient {
283
- private authFetch: AuthFetch
284
- private baseUrl: string
285
-
286
- constructor(baseUrl: string, wallet?: WalletClient) {
287
- this.baseUrl = baseUrl
288
- this.authFetch = new AuthFetch(wallet || new WalletClient('auto', 'localhost'))
289
- }
290
-
291
- async get(endpoint: string, options: any = {}): Promise<any> {
292
- return this.request('GET', endpoint, null, options)
293
- }
294
-
295
- async post(endpoint: string, data: any, options: any = {}): Promise<any> {
296
- return this.request('POST', endpoint, data, options)
297
- }
298
-
299
- async put(endpoint: string, data: any, options: any = {}): Promise<any> {
300
- return this.request('PUT', endpoint, data, options)
301
- }
302
-
303
- async delete(endpoint: string, options: any = {}): Promise<any> {
304
- return this.request('DELETE', endpoint, null, options)
305
- }
306
-
307
- private async request(
308
- method: string,
309
- endpoint: string,
310
- data: any = null,
311
- options: any = {}
312
- ): Promise<any> {
313
- const url = `${this.baseUrl}${endpoint}`
314
-
315
- const requestOptions: any = {
316
- method,
317
- headers: {
318
- 'Content-Type': 'application/json',
319
- ...options.headers
320
- }
321
- }
322
-
323
- if (data) {
324
- requestOptions.body = JSON.stringify(data)
325
- }
326
-
327
- try {
328
- console.log(`Making authenticated ${method} request to ${endpoint}`)
329
-
330
- const response = await this.authFetch.fetch(url, requestOptions)
331
-
332
- if (!response.ok) {
333
- throw new Error(`HTTP ${response.status}: ${response.statusText}`)
334
- }
335
-
336
- const contentType = response.headers.get('content-type')
337
- if (contentType && contentType.includes('application/json')) {
338
- return await response.json()
339
- } else {
340
- return await response.text()
341
- }
342
- } catch (error) {
343
- console.error(`Request failed for ${method} ${endpoint}:`, error)
344
- throw error
345
- }
346
- }
347
-
348
- async healthCheck(): Promise<boolean> {
349
- try {
350
- await this.get('/health')
351
- return true
352
- } catch (error) {
353
- return false
354
- }
355
- }
356
- }
357
-
358
- async function demonstrateSecureAPIClient() {
359
- // Create secure API client using real, testable endpoints
360
- const apiClient = new SecureAPIClient('https://httpbin.org')
361
-
362
- try {
363
- // Health check using a real endpoint
364
- console.log('Testing API client with real endpoints...')
365
-
366
- // Test GET request
367
- const getResult = await apiClient.get('/get?test=true&client=secure')
368
- console.log('✅ GET request successful')
369
- console.log('Request URL:', getResult.url)
370
- console.log('Query parameters received:', getResult.args)
371
-
372
- // Test POST request with data
373
- const postResult = await apiClient.post('/post', {
374
- user: 'demo-user',
375
- action: 'test-post',
376
- timestamp: new Date().toISOString(),
377
- authenticated: true
378
- })
379
- console.log('✅ POST request successful')
380
- console.log('Data sent:', postResult.json)
381
- console.log('Content-Type:', postResult.headers['Content-Type'])
382
-
383
- // Test PUT request
384
- const putResult = await apiClient.put('/put', {
385
- resource: 'user-settings',
386
- theme: 'dark',
387
- notifications: true,
388
- updated: new Date().toISOString()
389
- })
390
- console.log('✅ PUT request successful')
391
- console.log('PUT data received:', putResult.json)
392
-
393
- // Test DELETE request
394
- const deleteResult = await apiClient.delete('/delete')
395
- console.log('✅ DELETE request successful')
396
- console.log('DELETE method confirmed:', deleteResult.url)
397
-
398
- // Test custom headers
399
- const headersResult = await apiClient.get('/headers')
400
- console.log('✅ Headers test successful')
401
- console.log('Custom headers sent:', Object.keys(headersResult.headers).length)
402
-
403
- return {
404
- get: getResult,
405
- post: postResult,
406
- put: putResult,
407
- delete: deleteResult,
408
- headers: headersResult
409
- }
410
- } catch (error) {
411
- console.error('API operations failed:', error.message)
412
- if (error.message.includes('No wallet available')) {
413
- console.log('💡 Install MetaNet Desktop Wallet to test with real authentication')
414
- console.log(' The API calls work, but authentication requires a connected wallet')
415
- }
416
- }
417
- }
418
-
419
- demonstrateSecureAPIClient().catch(console.error)
420
- ```
421
-
422
- ### Multi-Peer Communication System
423
-
424
- ```typescript
425
- import { AuthFetch, WalletClient } from '@bsv/sdk'
426
-
427
- interface PeerInfo {
428
- url: string
429
- identity: string | null
430
- verified: boolean
431
- lastContact: Date
432
- }
433
-
434
- class PeerNetwork {
435
- private authFetch: AuthFetch
436
- private peers: Map<string, PeerInfo> = new Map()
437
-
438
- constructor(wallet?: WalletClient) {
439
- this.authFetch = new AuthFetch(wallet || new WalletClient('auto', 'localhost'))
440
- }
441
-
442
- async addPeer(peerUrl: string): Promise<boolean> {
443
- try {
444
- console.log(`Adding peer: ${peerUrl}`)
445
-
446
- // Verify peer identity
447
- const verification = await this.verifyPeer(peerUrl)
448
-
449
- const peerInfo: PeerInfo = {
450
- url: peerUrl,
451
- identity: verification.identity,
452
- verified: verification.verified,
453
- lastContact: new Date()
454
- }
455
-
456
- this.peers.set(peerUrl, peerInfo)
457
-
458
- console.log(`Peer ${peerUrl} ${verification.verified ? 'verified' : 'unverified'}`)
459
- return verification.verified
460
- } catch (error) {
461
- console.error(`Failed to add peer ${peerUrl}:`, error)
462
- return false
463
- }
464
- }
465
-
466
- private async verifyPeer(peerUrl: string): Promise<{
467
- verified: boolean
468
- identity: string | null
469
- }> {
470
- try {
471
- // Simple ping to verify peer is reachable
472
- const response = await this.authFetch.fetch(`${peerUrl}/ping`, {
473
- method: 'GET'
474
- })
475
-
476
- if (response.ok) {
477
- // In a real implementation, you would verify certificates here
478
- return { verified: true, identity: 'peer-identity' }
479
- } else {
480
- return { verified: false, identity: null }
481
- }
482
- } catch (error) {
483
- return { verified: false, identity: null }
484
- }
485
- }
486
-
487
- async broadcastMessage(message: any): Promise<{
488
- successful: string[]
489
- failed: string[]
490
- }> {
491
- const successful: string[] = []
492
- const failed: string[] = []
493
-
494
- console.log(`Broadcasting message to ${this.peers.size} peers`)
495
-
496
- const promises = Array.from(this.peers.entries()).map(async ([url, peerInfo]) => {
497
- try {
498
- const response = await this.authFetch.fetch(`${url}/message`, {
499
- method: 'POST',
500
- headers: {
501
- 'Content-Type': 'application/json'
502
- },
503
- body: JSON.stringify(message)
504
- })
505
-
506
- if (response.ok) {
507
- successful.push(url)
508
- // Update last contact
509
- peerInfo.lastContact = new Date()
510
- } else {
511
- failed.push(url)
512
- }
513
- } catch (error) {
514
- failed.push(url)
515
- console.error(`Failed to send message to ${url}:`, error)
516
- }
517
- })
518
-
519
- await Promise.all(promises)
520
-
521
- console.log(`Broadcast complete: ${successful.length} successful, ${failed.length} failed`)
522
- return { successful, failed }
523
- }
524
-
525
- async sendDirectMessage(peerUrl: string, message: any): Promise<any> {
526
- const peer = this.peers.get(peerUrl)
527
- if (!peer) {
528
- throw new Error(`Peer ${peerUrl} not found`)
529
- }
530
-
531
- if (!peer.verified) {
532
- throw new Error(`Peer ${peerUrl} not verified`)
533
- }
534
-
535
- try {
536
- const response = await this.authFetch.fetch(`${peerUrl}/direct-message`, {
537
- method: 'POST',
538
- headers: {
539
- 'Content-Type': 'application/json'
540
- },
541
- body: JSON.stringify(message)
542
- })
543
-
544
- if (response.ok) {
545
- peer.lastContact = new Date()
546
- return await response.json()
547
- } else {
548
- throw new Error(`HTTP ${response.status}: ${response.statusText}`)
549
- }
550
- } catch (error) {
551
- console.error(`Direct message to ${peerUrl} failed:`, error)
552
- throw error
553
- }
554
- }
555
-
556
- getPeerStatus(): { total: number; verified: number; unverified: number } {
557
- const total = this.peers.size
558
- let verified = 0
559
- let unverified = 0
560
-
561
- for (const peer of this.peers.values()) {
562
- if (peer.verified) {
563
- verified++
564
- } else {
565
- unverified++
566
- }
567
- }
568
-
569
- return { total, verified, unverified }
570
- }
571
-
572
- listPeers(): PeerInfo[] {
573
- return Array.from(this.peers.values())
574
- }
575
- }
576
-
577
- async function demonstratePeerNetwork() {
578
- const network = new PeerNetwork()
579
-
580
- // Add peers (replace with actual peer URLs)
581
- const peerUrls = [
582
- 'https://peer1.example.com',
583
- 'https://peer2.example.com',
584
- 'https://peer3.example.com'
585
- ]
586
-
587
- console.log('=== Setting up peer network ===')
588
-
589
- for (const peerUrl of peerUrls) {
590
- await network.addPeer(peerUrl)
591
- }
592
-
593
- const status = network.getPeerStatus()
594
- console.log('Network status:', status)
595
-
596
- // Broadcast message
597
- const broadcastMessage = {
598
- type: 'announcement',
599
- content: 'Hello from authenticated peer network!',
600
- timestamp: new Date().toISOString()
601
- }
602
-
603
- console.log('\n=== Broadcasting message ===')
604
- const broadcastResult = await network.broadcastMessage(broadcastMessage)
605
- console.log('Broadcast result:', broadcastResult)
606
-
607
- // Send direct message to first verified peer
608
- const peers = network.listPeers()
609
- const verifiedPeer = peers.find(p => p.verified)
610
-
611
- if (verifiedPeer) {
612
- console.log('\n=== Sending direct message ===')
613
- try {
614
- const directMessage = {
615
- type: 'direct',
616
- content: 'This is a direct authenticated message',
617
- timestamp: new Date().toISOString()
618
- }
619
-
620
- const response = await network.sendDirectMessage(verifiedPeer.url, directMessage)
621
- console.log('Direct message response:', response)
622
- } catch (error) {
623
- console.log('Direct message failed (expected in demo)')
624
- }
625
- }
626
-
627
- return { status, broadcastResult, peers }
628
- }
629
-
630
- demonstratePeerNetwork().catch(console.error)
631
- ```
632
-
633
- ## Advanced Authentication Patterns
634
-
635
- ### Session Management and Reconnection
636
-
637
- ```typescript
638
- import { AuthFetch, WalletClient } from '@bsv/sdk'
639
-
640
- class RobustAuthClient {
641
- private authFetch: AuthFetch
642
- private maxRetries: number = 3
643
- private retryDelay: number = 1000
644
-
645
- constructor(wallet?: WalletClient) {
646
- this.authFetch = new AuthFetch(wallet || new WalletClient('auto', 'localhost'))
647
- }
648
-
649
- async authenticatedRequest(
650
- url: string,
651
- options: any = {},
652
- retryCount: number = 0
653
- ): Promise<Response> {
654
- try {
655
- const response = await this.authFetch.fetch(url, options)
656
-
657
- if (response.status === 401 && retryCount < this.maxRetries) {
658
- console.log(`Authentication failed, retrying... (${retryCount + 1}/${this.maxRetries})`)
659
-
660
- // Wait and retry - AuthFetch will handle session management automatically
661
- await this.delay(this.retryDelay * (retryCount + 1))
662
-
663
- return this.authenticatedRequest(url, options, retryCount + 1)
664
- }
665
-
666
- return response
667
- } catch (error) {
668
- if (retryCount < this.maxRetries) {
669
- console.log(`Request failed, retrying... (${retryCount + 1}/${this.maxRetries})`)
670
- await this.delay(this.retryDelay * (retryCount + 1))
671
- return this.authenticatedRequest(url, options, retryCount + 1)
672
- }
673
-
674
- throw error
675
- }
676
- }
677
-
678
- private delay(ms: number): Promise<void> {
679
- return new Promise(resolve => setTimeout(resolve, ms))
680
- }
681
-
682
- async batchRequests(requests: Array<{
683
- url: string
684
- options?: any
685
- }>): Promise<Array<{
686
- success: boolean
687
- response?: any
688
- error?: string
689
- }>> {
690
- const results = await Promise.allSettled(
691
- requests.map(req => this.authenticatedRequest(req.url, req.options))
692
- )
693
-
694
- return results.map((result, index) => {
695
- if (result.status === 'fulfilled') {
696
- return {
697
- success: true,
698
- response: result.value
699
- }
700
- } else {
701
- return {
702
- success: false,
703
- error: result.reason.message
704
- }
705
- }
706
- })
707
- }
708
- }
709
-
710
- async function demonstrateRobustAuthentication() {
711
- const robustClient = new RobustAuthClient()
712
-
713
- console.log('=== Testing robust authentication ===')
714
-
715
- // Single request with retry logic
716
- try {
717
- const response = await robustClient.authenticatedRequest('https://api.example.com/data')
718
- console.log('Single request successful:', response.ok)
719
- } catch (error) {
720
- console.log('Single request failed after retries:', error.message)
721
- }
722
-
723
- // Batch requests
724
- const batchRequests = [
725
- { url: 'https://api.example.com/endpoint1' },
726
- { url: 'https://api.example.com/endpoint2' },
727
- { url: 'https://api.example.com/endpoint3' }
728
- ]
729
-
730
- console.log('\n=== Testing batch requests ===')
731
- const batchResults = await robustClient.batchRequests(batchRequests)
732
-
733
- batchResults.forEach((result, index) => {
734
- console.log(`Request ${index + 1}:`, result.success ? 'SUCCESS' : `FAILED - ${result.error}`)
735
- })
736
-
737
- return batchResults
738
- }
739
-
740
- demonstrateRobustAuthentication().catch(console.error)
741
- ```
742
-
743
- ## Error Handling and Debugging
744
-
745
- ### Comprehensive Error Handling
746
-
747
- ```typescript
748
- import { AuthFetch, WalletClient } from '@bsv/sdk'
749
-
750
- enum AuthErrorType {
751
- NETWORK_ERROR = 'network_error',
752
- AUTHENTICATION_FAILED = 'authentication_failed',
753
- CERTIFICATE_INVALID = 'certificate_invalid',
754
- PEER_UNREACHABLE = 'peer_unreachable',
755
- SESSION_EXPIRED = 'session_expired'
756
- }
757
-
758
- class AuthError extends Error {
759
- constructor(
760
- public type: AuthErrorType,
761
- message: string,
762
- public originalError?: Error
763
- ) {
764
- super(message)
765
- this.name = 'AuthError'
766
- }
767
- }
768
-
769
- class AuthFetchWithErrorHandling {
770
- private authFetch: AuthFetch
771
- private debugMode: boolean = false
772
-
773
- constructor(wallet?: WalletClient, debugMode: boolean = false) {
774
- this.authFetch = new AuthFetch(wallet || new WalletClient('auto', 'localhost'))
775
- this.debugMode = debugMode
776
- }
777
-
778
- async safeRequest(url: string, options: any = {}): Promise<{
779
- success: boolean
780
- data?: any
781
- error?: AuthError
782
- }> {
783
- try {
784
- if (this.debugMode) {
785
- console.log(`[DEBUG] Making request to: ${url}`)
786
- console.log(`[DEBUG] Options:`, JSON.stringify(options, null, 2))
787
- }
788
-
789
- const response = await this.authFetch.fetch(url, options)
790
-
791
- if (this.debugMode) {
792
- console.log(`[DEBUG] Response status: ${response.status}`)
793
- console.log(`[DEBUG] Response headers:`, Object.fromEntries(response.headers.entries()))
794
- }
795
-
796
- if (!response.ok) {
797
- const errorType = this.categorizeHttpError(response.status)
798
- const errorMessage = await this.extractErrorMessage(response)
799
-
800
- return {
801
- success: false,
802
- error: new AuthError(errorType, errorMessage)
803
- }
804
- }
805
-
806
- const data = await this.parseResponse(response)
807
- return { success: true, data }
808
-
809
- } catch (error) {
810
- if (this.debugMode) {
811
- console.log(`[DEBUG] Request failed:`, error)
812
- }
813
-
814
- const authError = this.categorizeError(error)
815
- return { success: false, error: authError }
816
- }
817
- }
818
-
819
- private categorizeHttpError(status: number): AuthErrorType {
820
- switch (status) {
821
- case 401:
822
- return AuthErrorType.AUTHENTICATION_FAILED
823
- case 403:
824
- return AuthErrorType.CERTIFICATE_INVALID
825
- case 408:
826
- case 504:
827
- return AuthErrorType.PEER_UNREACHABLE
828
- default:
829
- return AuthErrorType.NETWORK_ERROR
830
- }
831
- }
832
-
833
- private async extractErrorMessage(response: Response): Promise<string> {
834
- try {
835
- const contentType = response.headers.get('content-type')
836
- if (contentType && contentType.includes('application/json')) {
837
- const errorData = await response.json()
838
- return errorData.message || errorData.error || `HTTP ${response.status}`
839
- } else {
840
- return await response.text() || `HTTP ${response.status}`
841
- }
842
- } catch {
843
- return `HTTP ${response.status}: ${response.statusText}`
844
- }
845
- }
846
-
847
- private async parseResponse(response: Response): Promise<any> {
848
- const contentType = response.headers.get('content-type')
849
- if (contentType && contentType.includes('application/json')) {
850
- return await response.json()
851
- } else {
852
- return await response.text()
853
- }
854
- }
855
-
856
- private categorizeError(error: any): AuthError {
857
- if (error.name === 'TypeError' && error.message.includes('fetch')) {
858
- return new AuthError(
859
- AuthErrorType.NETWORK_ERROR,
860
- 'Network connection failed',
861
- error
862
- )
863
- }
864
-
865
- if (error.message.includes('certificate')) {
866
- return new AuthError(
867
- AuthErrorType.CERTIFICATE_INVALID,
868
- 'Certificate validation failed',
869
- error
870
- )
871
- }
872
-
873
- if (error.message.includes('session')) {
874
- return new AuthError(
875
- AuthErrorType.SESSION_EXPIRED,
876
- 'Authentication session expired',
877
- error
878
- )
879
- }
880
-
881
- return new AuthError(
882
- AuthErrorType.NETWORK_ERROR,
883
- error.message || 'Unknown error occurred',
884
- error
885
- )
886
- }
887
-
888
- async testConnectivity(urls: string[]): Promise<{
889
- reachable: string[]
890
- unreachable: string[]
891
- errors: Record<string, string>
892
- }> {
893
- const reachable: string[] = []
894
- const unreachable: string[] = []
895
- const errors: Record<string, string> = {}
896
-
897
- console.log(`Testing connectivity to ${urls.length} endpoints...`)
898
-
899
- const results = await Promise.allSettled(
900
- urls.map(url => this.safeRequest(`${url}/ping`))
901
- )
902
-
903
- results.forEach((result, index) => {
904
- const url = urls[index]
905
-
906
- if (result.status === 'fulfilled' && result.value.success) {
907
- reachable.push(url)
908
- console.log(` ${url} - reachable`)
909
- } else {
910
- unreachable.push(url)
911
- const error = result.status === 'fulfilled'
912
- ? result.value.error?.message || 'Unknown error'
913
- : result.reason.message
914
- errors[url] = error
915
- console.log(` ${url} - ${error}`)
916
- }
917
- })
918
-
919
- return { reachable, unreachable, errors }
920
- }
921
- }
922
-
923
- async function demonstrateErrorHandling() {
924
- const authClient = new AuthFetchWithErrorHandling(undefined, true) // Debug mode on
925
-
926
- console.log('=== Testing error handling ===')
927
-
928
- // Test various scenarios
929
- const testUrls = [
930
- 'https://httpbin.org/status/200', // Should succeed
931
- 'https://httpbin.org/status/401', // Authentication error
932
- 'https://httpbin.org/status/403', // Certificate error
933
- 'https://httpbin.org/status/500', // Server error
934
- 'https://invalid-domain-12345.com' // Network error
935
- ]
936
-
937
- for (const url of testUrls) {
938
- console.log(`\n--- Testing: ${url} ---`)
939
- const result = await authClient.safeRequest(url)
940
-
941
- if (result.success) {
942
- console.log(' Request successful')
943
- } else {
944
- console.log(` Request failed: ${result.error?.type} - ${result.error?.message}`)
945
- }
946
- }
947
-
948
- // Test connectivity to multiple endpoints
949
- console.log('\n=== Testing connectivity ===')
950
- const connectivityTest = await authClient.testConnectivity([
951
- 'https://httpbin.org',
952
- 'https://jsonplaceholder.typicode.com',
953
- 'https://invalid-endpoint.example.com'
954
- ])
955
-
956
- console.log('Connectivity results:', connectivityTest)
957
-
958
- return connectivityTest
959
- }
960
-
961
- demonstrateErrorHandling().catch(console.error)
962
- ```
963
-
964
- ## Conclusion
965
-
966
- Congratulations! You've successfully implemented a comprehensive authenticated communication system using the BSV TypeScript SDK. In this tutorial, you've learned how to:
967
-
968
- ### Core Concepts Mastered
969
-
970
- 1. **AuthFetch Integration**: Implemented authentication using identity-based signing
971
- 2. **Certificate Management**: Created and managed identity certificates for secure communication
972
- 3. **Request Signing**: Automatically signed requests with proper identity validation
973
- 4. **Error Handling**: Built robust error handling for authentication failures
974
- 5. **Network Resilience**: Implemented retry logic and connectivity testing
975
-
976
- ## Next Steps
977
-
978
- - Learn about [Identity Management](./identity-management.md) for advanced identity workflows
979
- - Explore [Authenticated API Communication](../guides/authenticated-api-communication.md) for server-side implementation
980
- - Understand [Security Best Practices](../guides/security-best-practices.md) for production deployments
981
-
982
- ## Additional Resources
983
-
984
- - [AuthFetch API Reference](../reference/auth.md)
985
- - [Identity Client Documentation](../reference/identity.md)
986
- - [BSV Identity Protocols](https://projectbabbage.com/docs/guides/identity)