@bsv/sdk 1.3.35 → 1.3.36

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bsv/sdk",
3
- "version": "1.3.35",
3
+ "version": "1.3.36",
4
4
  "type": "module",
5
5
  "description": "BSV Blockchain Software Development Kit",
6
6
  "main": "dist/cjs/mod.js",
@@ -211,15 +211,13 @@ describe('Peer class mutual authentication and certificate exchange', () => {
211
211
  const bobReceivedGeneralMessage = new Promise<void>((resolve) => {
212
212
  bob.listenForGeneralMessages((senderPublicKey, payload) => {
213
213
  (async () => {
214
- console.log('Bob received message:', Utils.toUTF8(payload))
215
214
  await bob.toPeer(Utils.toArray('Hello Alice!'), senderPublicKey)
216
215
  resolve()
217
- })().catch(e => console.log(e))
216
+ })().catch(e => { })
218
217
  })
219
218
  })
220
219
  const aliceReceivedGeneralMessage = new Promise<void>((resolve) => {
221
220
  alice.listenForGeneralMessages((senderPublicKey, payload) => {
222
- console.log('Alice received message:', Utils.toUTF8(payload))
223
221
  resolve()
224
222
  })
225
223
  })
@@ -260,29 +258,26 @@ describe('Peer class mutual authentication and certificate exchange', () => {
260
258
  const bobReceivedGeneralMessage = new Promise<void>((resolve) => {
261
259
  bob.listenForGeneralMessages((senderPublicKey, payload) => {
262
260
  (async () => {
263
- console.log('Bob 1 received message:', Utils.toUTF8(payload))
264
261
  await bob.toPeer(Utils.toArray('Hello Alice!'), senderPublicKey)
265
262
  resolve()
266
263
  bobMessageHandler(senderPublicKey, payload)
267
- })().catch(e => console.log(e))
264
+ })().catch(e => { })
268
265
  })
269
266
  })
270
267
  let aliceReceivedGeneralMessageOnFirstDevice = new Promise<void>((resolve) => {
271
268
  aliceFirstDevice.listenForGeneralMessages((senderPublicKey, payload) => {
272
269
  (async () => {
273
- console.log('Alice 1 received message:', Utils.toUTF8(payload))
274
270
  resolve()
275
271
  alice1MessageHandler(senderPublicKey, payload)
276
- })().catch(e => console.log(e))
272
+ })().catch(e => { })
277
273
  })
278
274
  })
279
275
  const aliceReceivedGeneralMessageOnOtherDevice = new Promise<void>((resolve) => {
280
276
  aliceOtherDevice.listenForGeneralMessages((senderPublicKey, payload) => {
281
277
  (async () => {
282
- console.log('Alice 2 received message:', Utils.toUTF8(payload))
283
278
  resolve()
284
279
  alice2MessageHandler(senderPublicKey, payload)
285
- })().catch(e => console.log(e))
280
+ })().catch(e => { })
286
281
  })
287
282
  })
288
283
 
@@ -333,10 +328,10 @@ describe('Peer class mutual authentication and certificate exchange', () => {
333
328
  // Use a for...of loop instead of forEach with an async callback.
334
329
  for (const cert of certificatesReceivedByBob) {
335
330
  // Decrypt to ensure it has the correct fields.
336
- const decryptedFields = await cert.decryptFields(walletB)
331
+ // const decryptedFields = await cert.decryptFields(walletB)
337
332
  if (cert.certifier !== 'bob') {
338
- console.log('Bob accepted the message:', Utils.toUTF8(payload))
339
- console.log('Decrypted fields:', decryptedFields)
333
+ // console.log('Bob accepted the message:', Utils.toUTF8(payload))
334
+ // console.log('Decrypted fields:', decryptedFields)
340
335
  }
341
336
  }
342
337
  }
@@ -640,7 +635,7 @@ describe('Peer class mutual authentication and certificate exchange', () => {
640
635
  resolve()
641
636
  }
642
637
  }
643
- })().catch(e => console.log(e))
638
+ })().catch(e => { })
644
639
  }
645
640
  )
646
641
  })
@@ -658,7 +653,7 @@ describe('Peer class mutual authentication and certificate exchange', () => {
658
653
  resolve()
659
654
  }
660
655
  }
661
- })().catch(e => console.log(e))
656
+ })().catch(e => { })
662
657
  }
663
658
  )
664
659
  })
@@ -672,7 +667,7 @@ describe('Peer class mutual authentication and certificate exchange', () => {
672
667
  senderPublicKey
673
668
  )
674
669
  resolve()
675
- })().catch(e => console.log(e))
670
+ })().catch(e => { })
676
671
  })
677
672
  })
678
673
 
@@ -766,7 +761,7 @@ describe('Peer class mutual authentication and certificate exchange', () => {
766
761
  resolve()
767
762
  }
768
763
  }
769
- })().catch(e => console.log(e))
764
+ })().catch(e => { })
770
765
  })
771
766
  })
772
767
 
@@ -783,7 +778,7 @@ describe('Peer class mutual authentication and certificate exchange', () => {
783
778
  resolve()
784
779
  }
785
780
  }
786
- })().catch(e => console.log(e))
781
+ })().catch(e => { })
787
782
  })
788
783
  })
789
784
 
@@ -6,14 +6,21 @@ import { SessionManager } from '../SessionManager.js'
6
6
  import { RequestedCertificateSet } from '../types.js'
7
7
  import { VerifiableCertificate } from '../certificates/VerifiableCertificate.js'
8
8
  import { Writer } from '../../primitives/utils.js'
9
+ import { getVerifiableCertificates } from '../utils/index.js'
9
10
 
10
- type SimplifiedFetchRequestOptions = {
11
- method?: string,
12
- headers?: Record<string, string>,
13
- body?: any,
11
+ interface SimplifiedFetchRequestOptions {
12
+ method?: string
13
+ headers?: Record<string, string>
14
+ body?: any
14
15
  retryCounter?: number
15
16
  }
16
- type AuthPeer = { peer: Peer, identityKey?: string, supportsMutualAuth?: boolean }
17
+
18
+ interface AuthPeer {
19
+ peer: Peer
20
+ identityKey?: string
21
+ supportsMutualAuth?: boolean
22
+ pendingCertificateRequests: Array<true>
23
+ }
17
24
 
18
25
  const PAYMENT_VERSION = '1.0'
19
26
 
@@ -26,11 +33,11 @@ const PAYMENT_VERSION = '1.0'
26
33
  * and sending BSV payment transactions when necessary.
27
34
  */
28
35
  export class AuthFetch {
29
- private sessionManager: SessionManager
30
- private wallet: WalletInterface
36
+ private readonly sessionManager: SessionManager
37
+ private readonly wallet: WalletInterface
31
38
  private callbacks: Record<string, { resolve: Function, reject: Function }> = {}
32
- private certificatesReceived: VerifiableCertificate[] = []
33
- private requestedCertificates?: RequestedCertificateSet
39
+ private readonly certificatesReceived: VerifiableCertificate[] = []
40
+ private readonly requestedCertificates?: RequestedCertificateSet
34
41
  peers: Record<string, AuthPeer> = {}
35
42
 
36
43
  /**
@@ -58,7 +65,7 @@ export class AuthFetch {
58
65
  * @throws Will throw an error if unsupported headers are used or other validation fails.
59
66
  */
60
67
  async fetch(url: string, config: SimplifiedFetchRequestOptions = {}): Promise<Response> {
61
- if (config.retryCounter) {
68
+ if (typeof config.retryCounter === 'number') {
62
69
  if (config.retryCounter <= 0) {
63
70
  throw new Error('Request failed after maximum number of retries.')
64
71
  }
@@ -79,13 +86,28 @@ export class AuthFetch {
79
86
  // Create a peer for the request
80
87
  const newTransport = new SimplifiedFetchTransport(baseURL)
81
88
  peerToUse = {
82
- peer: new Peer(this.wallet, newTransport, this.requestedCertificates, this.sessionManager)
89
+ peer: new Peer(this.wallet, newTransport, this.requestedCertificates, this.sessionManager),
90
+ pendingCertificateRequests: []
83
91
  }
84
92
  this.peers[baseURL] = peerToUse
85
- const callbackId = this.peers[baseURL].peer.listenForCertificatesReceived((senderPublicKey: string, certs: VerifiableCertificate[]) => {
93
+ this.peers[baseURL].peer.listenForCertificatesReceived((senderPublicKey: string, certs: VerifiableCertificate[]) => {
86
94
  this.certificatesReceived.push(...certs)
87
- // peerToUse.peer.stopListeningForCertificatesReceived()
88
95
  })
96
+ this.peers[baseURL].peer.listenForCertificatesRequested((async (verifier: string, requestedCertificates: RequestedCertificateSet) => {
97
+ try {
98
+ this.peers[baseURL].pendingCertificateRequests.push(true)
99
+ const certificatesToInclude = await getVerifiableCertificates(
100
+ this.wallet,
101
+ requestedCertificates,
102
+ verifier
103
+ )
104
+ await this.peers[baseURL].peer.sendCertificateResponse(verifier, certificatesToInclude)
105
+ } finally {
106
+ // Give the backend 500 ms to process the certificates we just sent, before releasing the queue entry
107
+ await new Promise(resolve => setTimeout(resolve, 500))
108
+ this.peers[baseURL].pendingCertificateRequests.shift()
109
+ }
110
+ }) as Function)
89
111
  } else {
90
112
  // Check if there's a session associated with this baseURL
91
113
  if (this.peers[baseURL].supportsMutualAuth === false) {
@@ -174,6 +196,20 @@ export class AuthFetch {
174
196
  delete this.callbacks[requestNonceAsBase64]
175
197
  })
176
198
 
199
+ // Before sending general messages to the peer, ensure that no certificate requests are pending.
200
+ // This way, the user would need to choose to either allow or reject the certificate request first.
201
+ // If the server has a resource that requires certificates to be sent before access would be granted,
202
+ // this makes sure the user has a chance to send the certificates before the resource is requested.
203
+ if (peerToUse.pendingCertificateRequests.length > 0) {
204
+ await new Promise(resolve => {
205
+ setInterval(() => {
206
+ if (peerToUse.pendingCertificateRequests.length === 0) {
207
+ resolve()
208
+ }
209
+ }, 100) // Check every 100 ms for the user to finish responding
210
+ })
211
+ }
212
+
177
213
  // Send the request, now that all listeners are set up
178
214
  await peerToUse.peer.toPeer(writer.toArray(), peerToUse.identityKey).catch(async error => {
179
215
  if (error.message.includes('Session not found for nonce')) {
@@ -217,7 +253,7 @@ export class AuthFetch {
217
253
  const baseURL = parsedUrl.origin
218
254
 
219
255
  let peerToUse: { peer: Peer; identityKey?: string }
220
- if (this.peers[baseURL]) {
256
+ if (typeof this.peers[baseURL] !== 'undefined') {
221
257
  peerToUse = { peer: this.peers[baseURL].peer }
222
258
  } else {
223
259
  const newTransport = new SimplifiedFetchTransport(baseURL)
@@ -233,7 +269,7 @@ export class AuthFetch {
233
269
  }
234
270
 
235
271
  // Return a promise that resolves when certificates are received
236
- return new Promise<VerifiableCertificate[]>(async (resolve, reject) => {
272
+ return await new Promise<VerifiableCertificate[]>((async (resolve, reject) => {
237
273
  // Set up the listener before making the request
238
274
  const callbackId = peerToUse.peer.listenForCertificatesReceived((_senderPublicKey: string, certs: VerifiableCertificate[]) => {
239
275
  peerToUse.peer.stopListeningForCertificatesReceived(callbackId)
@@ -248,7 +284,7 @@ export class AuthFetch {
248
284
  peerToUse.peer.stopListeningForCertificatesReceived(callbackId)
249
285
  reject(err)
250
286
  }
251
- })
287
+ }) as Function)
252
288
  }
253
289
 
254
290
  /**
@@ -513,4 +549,4 @@ export class AuthFetch {
513
549
  // 9. Fallback
514
550
  throw new Error('Unsupported body type in this SimplifiedFetch implementation.')
515
551
  }
516
- }
552
+ }
@@ -42,25 +42,38 @@ export class SimplifiedFetchTransport implements Transport {
42
42
  if (!this.onDataCallback) {
43
43
  throw new Error('Listen before you start speaking. God gave you two ears and one mouth for a reason.')
44
44
  }
45
-
46
45
  if (message.messageType !== 'general') {
47
- const response = await this.fetchClient(`${this.baseUrl}/.well-known/auth`, {
48
- method: 'POST',
49
- headers: {
50
- 'Content-Type': 'application/json'
51
- },
52
- body: JSON.stringify(message)
53
- })
54
- // Handle the response if data is received and callback is set
55
- if (response.ok && this.onDataCallback) {
56
- const responseMessage = await response.json()
57
- if (responseMessage?.status !== 'certificate received') {
58
- this.onDataCallback(responseMessage as AuthMessage)
46
+ return new Promise(async (resolve, reject) => {
47
+ try {
48
+ const responsePromise = this.fetchClient(`${this.baseUrl}/.well-known/auth`, {
49
+ method: 'POST',
50
+ headers: {
51
+ 'Content-Type': 'application/json'
52
+ },
53
+ body: JSON.stringify(message)
54
+ })
55
+
56
+ if (message.messageType !== "initialRequest") {
57
+ resolve()
58
+ }
59
+ const response = await responsePromise
60
+
61
+ // Handle the response if data is received and callback is set
62
+ if (response.ok && this.onDataCallback) {
63
+ const responseMessage = await response.json()
64
+ this.onDataCallback(responseMessage as AuthMessage)
65
+ } else {
66
+ // Server may be a non authenticated server
67
+ throw new Error('HTTP server failed to authenticate')
68
+ }
69
+ if (message.messageType === "initialRequest") {
70
+ resolve()
71
+ }
72
+ } catch (e) {
73
+ reject(e)
74
+ return
59
75
  }
60
- } else {
61
- // Server may be a non authenticated server
62
- throw new Error('HTTP server failed to authenticate')
63
- }
76
+ })
64
77
  } else {
65
78
  // Parse message payload
66
79
  const httpRequest = this.deserializeRequestPayload(message.payload)
@@ -105,7 +118,6 @@ export class SimplifiedFetchTransport implements Transport {
105
118
  }
106
119
  }
107
120
 
108
-
109
121
  // Send the actual fetch request to the server
110
122
  const response = await this.fetchClient(url, {
111
123
  method: httpRequestWithAuthHeaders.method,
@@ -141,8 +141,6 @@ describe('verifyNonce', () => {
141
141
  counterparty: (await serverWallet.getPublicKey({ identityKey: true }))
142
142
  .publicKey
143
143
  })
144
-
145
- console.log(Utils.toBase64(serialNumber))
146
144
  expect(valid).toEqual(true)
147
145
  })
148
146
  })
@@ -9,7 +9,7 @@ import MerklePath from '../MerklePath'
9
9
  jest.setTimeout(60000) // Increase timeout for benchmarking tests if necessary
10
10
 
11
11
  // Helper function to measure execution time
12
- async function measureTime (fn: () => Promise<void>): Promise<number> {
12
+ async function measureTime(fn: () => Promise<void>): Promise<number> {
13
13
  const start = process.hrtime()
14
14
  await fn()
15
15
  const diff = process.hrtime(start)
@@ -113,11 +113,6 @@ describe('Transaction Verification Benchmark', () => {
113
113
  const verified = await tx.verify('scripts only')
114
114
  expect(verified).toBe(true)
115
115
  })
116
- console.log(
117
- `Verification time for wide transaction with ${inputCount} inputs: ${timeTaken.toFixed(
118
- 2
119
- )} ms`
120
- )
121
116
  })
122
117
 
123
118
  it('verifies a large transaction with many inputs and outputs', async () => {
@@ -172,11 +167,6 @@ describe('Transaction Verification Benchmark', () => {
172
167
  const verified = await tx.verify('scripts only')
173
168
  expect(verified).toBe(true)
174
169
  })
175
- console.log(
176
- `Verification time for large transaction with ${inputCount} inputs and ${outputCount} outputs: ${timeTaken.toFixed(
177
- 2
178
- )} ms`
179
- )
180
170
  })
181
171
 
182
172
  it('verifies a transaction with nested inputs (complex graph)', async () => {
@@ -243,10 +233,5 @@ describe('Transaction Verification Benchmark', () => {
243
233
  const verified = await finalTx.verify('scripts only')
244
234
  expect(verified).toBe(true)
245
235
  })
246
- console.log(
247
- `Verification time for nested inputs with depth ${depth} and fan-out ${fanOut}: ${timeTaken.toFixed(
248
- 2
249
- )} ms`
250
- )
251
236
  })
252
237
  })