@bsv/sdk 1.3.1 → 1.3.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/docs/auth.md CHANGED
@@ -134,7 +134,7 @@ See also: [RequestedCertificateSet](#interface-requestedcertificateset), [Sessio
134
134
 
135
135
  #### Constructor
136
136
 
137
- Constructs a new SimplifiedFetch instance.
137
+ Constructs a new AuthFetch instance.
138
138
 
139
139
  ```ts
140
140
  constructor(wallet: Wallet, requestedCertificates?: RequestedCertificateSet, sessionManager?: SessionManager)
@@ -459,7 +459,7 @@ and sending and receiving general messages over a transport layer.
459
459
  export class Peer {
460
460
  public sessionManager: SessionManager;
461
461
  certificatesToRequest: RequestedCertificateSet;
462
- constructor(wallet: Wallet, transport: Transport, certificatesToRequest?: RequestedCertificateSet, sessionManager?: SessionManager)
462
+ constructor(wallet: Wallet, transport: Transport, certificatesToRequest?: RequestedCertificateSet, sessionManager?: SessionManager, autoPersistLastSession?: boolean)
463
463
  async toPeer(message: number[], identityKey?: string, maxWaitTime?: number): Promise<void>
464
464
  async requestCertificates(certificatesToRequest: RequestedCertificateSet, identityKey?: string, maxWaitTime = 10000): Promise<void>
465
465
  async getAuthenticatedSession(identityKey?: string, maxWaitTime?: number): Promise<PeerSession>
@@ -485,7 +485,7 @@ See also: [AuthMessage](#interface-authmessage), [PeerSession](#interface-peerse
485
485
  Creates a new Peer instance
486
486
 
487
487
  ```ts
488
- constructor(wallet: Wallet, transport: Transport, certificatesToRequest?: RequestedCertificateSet, sessionManager?: SessionManager)
488
+ constructor(wallet: Wallet, transport: Transport, certificatesToRequest?: RequestedCertificateSet, sessionManager?: SessionManager, autoPersistLastSession?: boolean)
489
489
  ```
490
490
  See also: [RequestedCertificateSet](#interface-requestedcertificateset), [SessionManager](#class-sessionmanager), [Transport](#interface-transport), [Wallet](#interface-wallet)
491
491
 
@@ -499,6 +499,8 @@ Argument Details
499
499
  + Optional set of certificates to request from a peer during the initial handshake.
500
500
  + **sessionManager**
501
501
  + Optional SessionManager to be used for managing peer sessions.
502
+ + **autoPersistLastSession**
503
+ + Whether to auto-persist the session with the last-interacted-with peer. Defaults to true.
502
504
 
503
505
  #### Method getAuthenticatedSession
504
506
 
@@ -822,7 +824,7 @@ This class integrates with fetch to send and receive authenticated messages betw
822
824
  export class SimplifiedFetchTransport implements Transport {
823
825
  fetchClient: typeof fetch;
824
826
  baseUrl: string;
825
- constructor(baseUrl: string, fetchClient = fetch)
827
+ constructor(baseUrl: string, fetchClient = defaultFetch)
826
828
  async send(message: AuthMessage): Promise<void>
827
829
  async onData(callback: (message: AuthMessage) => Promise<void>): Promise<void>
828
830
  deserializeRequestPayload(payload: number[]): {
@@ -846,7 +848,7 @@ See also: [AuthMessage](#interface-authmessage), [Transport](#interface-transpor
846
848
  Constructs a new instance of SimplifiedFetchTransport.
847
849
 
848
850
  ```ts
849
- constructor(baseUrl: string, fetchClient = fetch)
851
+ constructor(baseUrl: string, fetchClient = defaultFetch)
850
852
  ```
851
853
 
852
854
  Argument Details
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bsv/sdk",
3
- "version": "1.3.1",
3
+ "version": "1.3.2",
4
4
  "type": "module",
5
5
  "description": "BSV Blockchain Software Development Kit",
6
6
  "main": "dist/cjs/mod.js",
package/src/auth/Peer.ts CHANGED
@@ -24,6 +24,12 @@ export class Peer {
24
24
  // Single shared counter for all callback types
25
25
  private callbackIdCounter: number = 0
26
26
 
27
+ // Whether to auto-persist the session with the last-interacted-with peer
28
+ private autoPersistLastSession: boolean = true
29
+
30
+ // Last-interacted-with peer identity key
31
+ private lastInteractedWithPeer: string | undefined
32
+
27
33
  /**
28
34
  * Creates a new Peer instance
29
35
  *
@@ -31,18 +37,25 @@ export class Peer {
31
37
  * @param {Transport} transport - The transport mechanism used for sending and receiving messages.
32
38
  * @param {RequestedCertificateSet} [certificatesToRequest] - Optional set of certificates to request from a peer during the initial handshake.
33
39
  * @param {SessionManager} [sessionManager] - Optional SessionManager to be used for managing peer sessions.
40
+ * @param {boolean} [autoPersistLastSession] - Whether to auto-persist the session with the last-interacted-with peer. Defaults to true.
34
41
  */
35
- constructor (
42
+ constructor(
36
43
  wallet: Wallet,
37
44
  transport: Transport,
38
45
  certificatesToRequest?: RequestedCertificateSet,
39
- sessionManager?: SessionManager
46
+ sessionManager?: SessionManager,
47
+ autoPersistLastSession?: boolean
40
48
  ) {
41
49
  this.wallet = wallet
42
50
  this.transport = transport
43
51
  this.certificatesToRequest = certificatesToRequest ?? { certifiers: [], types: {} }
44
52
  this.transport.onData(this.handleIncomingMessage.bind(this))
45
53
  this.sessionManager = sessionManager || new SessionManager()
54
+ if (autoPersistLastSession === false) {
55
+ this.autoPersistLastSession = false
56
+ } else {
57
+ this.autoPersistLastSession = true
58
+ }
46
59
  }
47
60
 
48
61
  /**
@@ -53,7 +66,10 @@ export class Peer {
53
66
  * @returns {Promise<void>}
54
67
  * @throws Will throw an error if the message fails to send.
55
68
  */
56
- async toPeer (message: number[], identityKey?: string, maxWaitTime?: number): Promise<void> {
69
+ async toPeer(message: number[], identityKey?: string, maxWaitTime?: number): Promise<void> {
70
+ if (this.autoPersistLastSession && this.lastInteractedWithPeer && typeof identityKey !== 'string') {
71
+ identityKey = this.lastInteractedWithPeer
72
+ }
57
73
  const peerSession = await this.getAuthenticatedSession(identityKey, maxWaitTime)
58
74
 
59
75
  // Prepare the general message
@@ -95,7 +111,7 @@ export class Peer {
95
111
  * @returns {Promise<void>} Resolves if the certificate request message is successfully sent.
96
112
  * @throws Will throw an error if the peer session is not authenticated or if sending the request fails.
97
113
  */
98
- async requestCertificates (certificatesToRequest: RequestedCertificateSet, identityKey?: string, maxWaitTime = 10000): Promise<void> {
114
+ async requestCertificates(certificatesToRequest: RequestedCertificateSet, identityKey?: string, maxWaitTime = 10000): Promise<void> {
99
115
  const peerSession = await this.getAuthenticatedSession(identityKey, maxWaitTime)
100
116
 
101
117
  // Prepare the general message
@@ -136,7 +152,7 @@ export class Peer {
136
152
  * @returns {Promise<PeerSession>} - A promise that resolves with an authenticated `PeerSession`.
137
153
  * @throws {Error} - Throws an error if the transport is not connected or if the handshake fails.
138
154
  */
139
- async getAuthenticatedSession (identityKey?: string, maxWaitTime?: number): Promise<PeerSession> {
155
+ async getAuthenticatedSession(identityKey?: string, maxWaitTime?: number): Promise<PeerSession> {
140
156
  if (!this.transport) {
141
157
  throw new Error('Peer transport is not connected!')
142
158
  }
@@ -159,7 +175,7 @@ export class Peer {
159
175
  * @param {(senderPublicKey: string, payload: number[]) => void} callback - The function to call when a general message is received.
160
176
  * @returns {number} The ID of the callback listener.
161
177
  */
162
- listenForGeneralMessages (callback: (senderPublicKey: string, payload: number[]) => void): number {
178
+ listenForGeneralMessages(callback: (senderPublicKey: string, payload: number[]) => void): number {
163
179
  const callbackID = this.callbackIdCounter++
164
180
  this.onGeneralMessageReceivedCallbacks.set(callbackID, callback)
165
181
  return callbackID
@@ -170,7 +186,7 @@ export class Peer {
170
186
  *
171
187
  * @param {number} callbackID - The ID of the callback to remove.
172
188
  */
173
- stopListeningForGeneralMessages (callbackID: number): void {
189
+ stopListeningForGeneralMessages(callbackID: number): void {
174
190
  this.onGeneralMessageReceivedCallbacks.delete(callbackID)
175
191
  }
176
192
 
@@ -180,7 +196,7 @@ export class Peer {
180
196
  * @param {(certs: VerifiableCertificate[]) => void} callback - The function to call when certificates are received.
181
197
  * @returns {number} The ID of the callback listener.
182
198
  */
183
- listenForCertificatesReceived (callback: (senderPublicKey: string, certs: VerifiableCertificate[]) => void): number {
199
+ listenForCertificatesReceived(callback: (senderPublicKey: string, certs: VerifiableCertificate[]) => void): number {
184
200
  const callbackID = this.callbackIdCounter++
185
201
  this.onCertificatesReceivedCallbacks.set(callbackID, callback)
186
202
  return callbackID
@@ -191,7 +207,7 @@ export class Peer {
191
207
  *
192
208
  * @param {number} callbackID - The ID of the certificates received callback to cancel.
193
209
  */
194
- stopListeningForCertificatesReceived (callbackID: number): void {
210
+ stopListeningForCertificatesReceived(callbackID: number): void {
195
211
  this.onCertificatesReceivedCallbacks.delete(callbackID)
196
212
  }
197
213
 
@@ -201,7 +217,7 @@ export class Peer {
201
217
  * @param {(requestedCertificates: RequestedCertificateSet) => void} callback - The function to call when a certificate request is received
202
218
  * @returns {number} The ID of the callback listener.
203
219
  */
204
- listenForCertificatesRequested (callback: (senderPublicKey: string, requestedCertificates: RequestedCertificateSet) => void): number {
220
+ listenForCertificatesRequested(callback: (senderPublicKey: string, requestedCertificates: RequestedCertificateSet) => void): number {
205
221
  const callbackID = this.callbackIdCounter++
206
222
  this.onCertificateRequestReceivedCallbacks.set(callbackID, callback)
207
223
  return callbackID
@@ -212,7 +228,7 @@ export class Peer {
212
228
  *
213
229
  * @param {number} callbackID - The ID of the requested certificates callback to cancel.
214
230
  */
215
- stopListeningForCertificatesRequested (callbackID: number): void {
231
+ stopListeningForCertificatesRequested(callbackID: number): void {
216
232
  this.onCertificateRequestReceivedCallbacks.delete(callbackID)
217
233
  }
218
234
 
@@ -223,7 +239,7 @@ export class Peer {
223
239
  * @param {string} [identityKey] - The identity public key of the peer.
224
240
  * @returns {Promise<string>} A promise that resolves to the session nonce.
225
241
  */
226
- private async initiateHandshake (identityKey?: string, maxWaitTime = 10000): Promise<string> {
242
+ private async initiateHandshake(identityKey?: string, maxWaitTime = 10000): Promise<string> {
227
243
  const sessionNonce = await createNonce(this.wallet) // Initial request nonce
228
244
  this.sessionManager.addSession({
229
245
  isAuthenticated: false,
@@ -249,7 +265,7 @@ export class Peer {
249
265
  * @param {string} sessionNonce - The session nonce created in the initial request.
250
266
  * @returns {Promise<string>} A promise that resolves with the session nonce when the initial response is received.
251
267
  */
252
- private async waitForInitialResponse (sessionNonce: string, maxWaitTime = 10000): Promise<string> {
268
+ private async waitForInitialResponse(sessionNonce: string, maxWaitTime = 10000): Promise<string> {
253
269
  return await new Promise((resolve, reject) => {
254
270
  const callbackID = this.listenForInitialResponse(sessionNonce, (sessionNonce) => {
255
271
  clearTimeout(timeoutHandle)
@@ -272,7 +288,7 @@ export class Peer {
272
288
  * @param {(sessionNonce: string) => void} callback - The callback to invoke when the initial response is received.
273
289
  * @returns {number} The ID of the callback listener.
274
290
  */
275
- private listenForInitialResponse (sessionNonce: string, callback: (sessionNonce: string) => void) {
291
+ private listenForInitialResponse(sessionNonce: string, callback: (sessionNonce: string) => void) {
276
292
  const callbackID = this.callbackIdCounter++
277
293
  this.onInitialResponseReceivedCallbacks.set(callbackID, { callback, sessionNonce })
278
294
  return callbackID
@@ -284,7 +300,7 @@ export class Peer {
284
300
  * @private
285
301
  * @param {number} callbackID - The ID of the callback to remove.
286
302
  */
287
- private stopListeningForInitialResponses (callbackID: number) {
303
+ private stopListeningForInitialResponses(callbackID: number) {
288
304
  this.onInitialResponseReceivedCallbacks.delete(callbackID)
289
305
  }
290
306
 
@@ -294,7 +310,7 @@ export class Peer {
294
310
  * @param {AuthMessage} message - The incoming message to process.
295
311
  * @returns {Promise<void>}
296
312
  */
297
- private async handleIncomingMessage (message: AuthMessage): Promise<void> {
313
+ private async handleIncomingMessage(message: AuthMessage): Promise<void> {
298
314
  if (!message.version || message.version !== AUTH_VERSION) {
299
315
  console.error(`Invalid message auth version! Received: ${message.version}, expected: ${AUTH_VERSION}`)
300
316
  return
@@ -327,7 +343,7 @@ export class Peer {
327
343
  * @param {AuthMessage} message - The incoming initial request message.
328
344
  * @returns {Promise<void>}
329
345
  */
330
- async processInitialRequest (message: AuthMessage) {
346
+ async processInitialRequest(message: AuthMessage) {
331
347
  if (!message.identityKey || !message.initialNonce) {
332
348
  throw new Error('Missing required fields in initialResponse message.')
333
349
  }
@@ -374,6 +390,11 @@ export class Peer {
374
390
  signature
375
391
  }
376
392
 
393
+ // For security, only set the last-interacted-with peer here if this is the first peer we've interacted with.
394
+ if (!this.lastInteractedWithPeer) {
395
+ this.lastInteractedWithPeer = message.identityKey
396
+ }
397
+
377
398
  await this.transport.send(initialResponseMessage)
378
399
  }
379
400
 
@@ -385,7 +406,7 @@ export class Peer {
385
406
  * @returns {Promise<void>}
386
407
  * @throws Will throw an error if nonce verification or signature verification fails.
387
408
  */
388
- private async processInitialResponse (message: AuthMessage) {
409
+ private async processInitialResponse(message: AuthMessage) {
389
410
  const validNonce = await verifyNonce(message.yourNonce, this.wallet)
390
411
  if (!validNonce) {
391
412
  throw new Error(`Initial response nonce verification failed from peer: ${message.identityKey}`)
@@ -426,6 +447,8 @@ export class Peer {
426
447
  )
427
448
  }
428
449
 
450
+ this.lastInteractedWithPeer = message.identityKey
451
+
429
452
  this.onInitialResponseReceivedCallbacks.forEach((entry) => {
430
453
  if (entry && entry.sessionNonce === peerSession.sessionNonce) {
431
454
  entry.callback(peerSession.sessionNonce)
@@ -455,7 +478,7 @@ export class Peer {
455
478
  * @param {AuthMessage} message - The certificate request message received from the peer.
456
479
  * @throws {Error} Throws an error if nonce verification fails, or the message signature is invalid.
457
480
  */
458
- private async processCertificateRequest (message: AuthMessage) {
481
+ private async processCertificateRequest(message: AuthMessage) {
459
482
  const validNonce = await verifyNonce(message.yourNonce, this.wallet)
460
483
  if (!validNonce) {
461
484
  throw new Error(`Unable to verify nonce for certificate request message from: ${message.identityKey}`)
@@ -497,7 +520,7 @@ export class Peer {
497
520
  *
498
521
  * @throws {Error} Throws an error if the peer session could not be authenticated or if message signing fails.
499
522
  */
500
- async sendCertificateResponse (
523
+ async sendCertificateResponse(
501
524
  verifierIdentityKey: string,
502
525
  certificates: VerifiableCertificate[]
503
526
  ) {
@@ -536,7 +559,7 @@ export class Peer {
536
559
  * @returns {Promise<void>}
537
560
  * @throws Will throw an error if nonce verification or signature verification fails.
538
561
  */
539
- private async processCertificateResponse (
562
+ private async processCertificateResponse(
540
563
  message: AuthMessage
541
564
  ) {
542
565
  const validNonce = await verifyNonce(message.yourNonce, this.wallet)
@@ -574,7 +597,7 @@ export class Peer {
574
597
  * @returns {Promise<void>}
575
598
  * @throws Will throw an error if nonce verification or signature verification fails.
576
599
  */
577
- private async processGeneralMessage (message: AuthMessage) {
600
+ private async processGeneralMessage(message: AuthMessage) {
578
601
  const validNonce = await verifyNonce(message.yourNonce, this.wallet)
579
602
  if (!validNonce) {
580
603
  throw new Error(`Unable to verify nonce for general message from: ${message.identityKey}`)
@@ -593,6 +616,8 @@ export class Peer {
593
616
  throw new Error(`Invalid signature in generalMessage from ${peerSession.peerIdentityKey}`)
594
617
  }
595
618
 
619
+ this.lastInteractedWithPeer = message.identityKey
620
+
596
621
  this.onGeneralMessageReceivedCallbacks.forEach(callback => {
597
622
  callback(message.identityKey, message.payload)
598
623
  })