@bsv/sdk 2.1.1 → 2.1.3

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 (79) hide show
  1. package/dist/cjs/package.json +1 -1
  2. package/dist/cjs/src/auth/Peer.js +21 -18
  3. package/dist/cjs/src/auth/Peer.js.map +1 -1
  4. package/dist/cjs/src/auth/SessionManager.js.map +1 -1
  5. package/dist/cjs/src/auth/clients/AuthFetch.js +4 -1
  6. package/dist/cjs/src/auth/clients/AuthFetch.js.map +1 -1
  7. package/dist/cjs/src/identity/ContactsManager.js +44 -6
  8. package/dist/cjs/src/identity/ContactsManager.js.map +1 -1
  9. package/dist/cjs/src/identity/IdentityClient.js +106 -37
  10. package/dist/cjs/src/identity/IdentityClient.js.map +1 -1
  11. package/dist/cjs/src/overlay-tools/LookupResolver.js +180 -82
  12. package/dist/cjs/src/overlay-tools/LookupResolver.js.map +1 -1
  13. package/dist/cjs/src/primitives/Hash.js +173 -50
  14. package/dist/cjs/src/primitives/Hash.js.map +1 -1
  15. package/dist/cjs/src/primitives/SymmetricKey.js +123 -1
  16. package/dist/cjs/src/primitives/SymmetricKey.js.map +1 -1
  17. package/dist/cjs/src/transaction/MerklePath.js +1 -1
  18. package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
  19. package/dist/esm/src/auth/Peer.js +28 -18
  20. package/dist/esm/src/auth/Peer.js.map +1 -1
  21. package/dist/esm/src/auth/SessionManager.js.map +1 -1
  22. package/dist/esm/src/auth/clients/AuthFetch.js +4 -1
  23. package/dist/esm/src/auth/clients/AuthFetch.js.map +1 -1
  24. package/dist/esm/src/identity/ContactsManager.js +44 -6
  25. package/dist/esm/src/identity/ContactsManager.js.map +1 -1
  26. package/dist/esm/src/identity/IdentityClient.js +106 -37
  27. package/dist/esm/src/identity/IdentityClient.js.map +1 -1
  28. package/dist/esm/src/overlay-tools/LookupResolver.js +180 -82
  29. package/dist/esm/src/overlay-tools/LookupResolver.js.map +1 -1
  30. package/dist/esm/src/primitives/Hash.js +177 -50
  31. package/dist/esm/src/primitives/Hash.js.map +1 -1
  32. package/dist/esm/src/primitives/SymmetricKey.js +123 -1
  33. package/dist/esm/src/primitives/SymmetricKey.js.map +1 -1
  34. package/dist/esm/src/transaction/MerklePath.js +1 -1
  35. package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
  36. package/dist/types/src/auth/Peer.d.ts +3 -3
  37. package/dist/types/src/auth/Peer.d.ts.map +1 -1
  38. package/dist/types/src/auth/SessionManager.d.ts +21 -0
  39. package/dist/types/src/auth/SessionManager.d.ts.map +1 -1
  40. package/dist/types/src/auth/clients/AuthFetch.d.ts +2 -2
  41. package/dist/types/src/auth/clients/AuthFetch.d.ts.map +1 -1
  42. package/dist/types/src/identity/ContactsManager.d.ts +13 -2
  43. package/dist/types/src/identity/ContactsManager.d.ts.map +1 -1
  44. package/dist/types/src/identity/IdentityClient.d.ts +50 -24
  45. package/dist/types/src/identity/IdentityClient.d.ts.map +1 -1
  46. package/dist/types/src/overlay-tools/LookupResolver.d.ts +15 -1
  47. package/dist/types/src/overlay-tools/LookupResolver.d.ts.map +1 -1
  48. package/dist/types/src/primitives/Hash.d.ts +21 -16
  49. package/dist/types/src/primitives/Hash.d.ts.map +1 -1
  50. package/dist/types/src/primitives/SymmetricKey.d.ts.map +1 -1
  51. package/dist/types/src/wallet/Wallet.interfaces.d.ts +16 -1
  52. package/dist/types/src/wallet/Wallet.interfaces.d.ts.map +1 -1
  53. package/dist/types/src/wallet/WalletClient.d.ts +1 -1
  54. package/dist/types/src/wallet/WalletClient.d.ts.map +1 -1
  55. package/dist/types/src/wallet/substrates/window.CWI.d.ts +1 -1
  56. package/dist/types/src/wallet/substrates/window.CWI.d.ts.map +1 -1
  57. package/dist/types/tsconfig.types.tsbuildinfo +1 -1
  58. package/dist/umd/bundle.js +4 -4
  59. package/package.json +1 -1
  60. package/src/auth/Peer.ts +30 -20
  61. package/src/auth/SessionManager.ts +22 -0
  62. package/src/auth/__tests/Peer.test.ts +47 -1
  63. package/src/auth/clients/AuthFetch.ts +6 -3
  64. package/src/identity/ContactsManager.ts +47 -6
  65. package/src/identity/IdentityClient.ts +137 -53
  66. package/src/identity/__tests/IdentityClient.additional.test.ts +150 -1
  67. package/src/identity/__tests/IdentityClient.test.ts +4 -4
  68. package/src/overlay-tools/LookupResolver.ts +191 -77
  69. package/src/overlay-tools/__tests/LookupResolver.additional.test.ts +90 -0
  70. package/src/primitives/Hash.ts +232 -96
  71. package/src/primitives/SymmetricKey.ts +145 -1
  72. package/src/primitives/__tests/Hash.additional.test.ts +65 -0
  73. package/src/primitives/__tests/Hash.test.ts +6 -1
  74. package/src/script/__tests/Spend.test.ts +45 -4
  75. package/src/transaction/MerklePath.ts +1 -1
  76. package/src/transaction/__tests/Transaction.test.ts +17 -0
  77. package/src/wallet/Wallet.interfaces.ts +16 -1
  78. package/src/wallet/WalletClient.ts +1 -1
  79. package/src/wallet/substrates/window.CWI.ts +1 -1
@@ -1,5 +1,10 @@
1
+ import { createHash, createHmac } from 'node:crypto'
1
2
  import { SHA1HMAC, SHA512HMAC, pbkdf2 } from '../../primitives/Hash'
2
3
 
4
+ function toHex (arr: number[]): string {
5
+ return Buffer.from(arr).toString('hex')
6
+ }
7
+
3
8
  describe('Hash – additional coverage', () => {
4
9
  describe('SHA1HMAC', () => {
5
10
  it('produces a correct HMAC-SHA1 digest', () => {
@@ -56,4 +61,64 @@ describe('Hash – additional coverage', () => {
56
61
  )
57
62
  })
58
63
  })
64
+
65
+ describe('pure-TS fallback parity', () => {
66
+ const msg = new Uint8Array([
67
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
68
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
69
+ 0xff, 0xfe, 0xfd, 0xfc
70
+ ])
71
+ const key = new Uint8Array([0xde, 0xad, 0xbe, 0xef])
72
+
73
+ const expectedSha256 = createHash('sha256').update(msg).digest('hex')
74
+ const expectedSha512 = createHash('sha512').update(msg).digest('hex')
75
+ const expectedRipemd160 = createHash('ripemd160').update(msg).digest('hex')
76
+ const expectedHash256 = createHash('sha256')
77
+ .update(createHash('sha256').update(msg).digest())
78
+ .digest('hex')
79
+ const expectedHash160 = createHash('ripemd160')
80
+ .update(createHash('sha256').update(msg).digest())
81
+ .digest('hex')
82
+ const expectedSha256Hmac = createHmac('sha256', key)
83
+ .update(msg)
84
+ .digest('hex')
85
+ const expectedSha512Hmac = createHmac('sha512', key)
86
+ .update(msg)
87
+ .digest('hex')
88
+
89
+ it('matches native digests when node:crypto is unavailable', () => {
90
+ const originalGetBuiltin = (process as any).getBuiltinModule
91
+ ;(process as any).getBuiltinModule = (): never => {
92
+ throw new Error('blocked for test')
93
+ }
94
+ try {
95
+ jest.isolateModules(() => {
96
+ jest.doMock('node:crypto', () => {
97
+ throw new Error('blocked for test')
98
+ })
99
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
100
+ const fallback = require('../../primitives/Hash')
101
+ expect(toHex(fallback.sha256(Array.from(msg)))).toBe(expectedSha256)
102
+ expect(toHex(fallback.sha512(Array.from(msg)))).toBe(expectedSha512)
103
+ expect(toHex(fallback.ripemd160(Array.from(msg)))).toBe(
104
+ expectedRipemd160
105
+ )
106
+ expect(toHex(fallback.hash256(Array.from(msg)))).toBe(
107
+ expectedHash256
108
+ )
109
+ expect(toHex(fallback.hash160(Array.from(msg)))).toBe(
110
+ expectedHash160
111
+ )
112
+ expect(
113
+ toHex(fallback.sha256hmac(Array.from(key), Array.from(msg)))
114
+ ).toBe(expectedSha256Hmac)
115
+ expect(
116
+ toHex(fallback.sha512hmac(Array.from(key), Array.from(msg)))
117
+ ).toBe(expectedSha512Hmac)
118
+ })
119
+ } finally {
120
+ ;(process as any).getBuiltinModule = originalGetBuiltin
121
+ }
122
+ })
123
+ })
59
124
  })
@@ -42,6 +42,11 @@ describe('Hash', function () {
42
42
  ])
43
43
  })
44
44
 
45
+ it('preserves SDK hex normalization before hashing', function () {
46
+ expect(hash.sha256('abc', 'hex')).toEqual(hash.sha256([0x0a, 0xbc]))
47
+ expect(() => hash.sha256('zz', 'hex')).toThrow('Invalid hex string')
48
+ })
49
+
45
50
  it('should support ripemd160', function () {
46
51
  test(hash.RIPEMD160, [
47
52
  ['', '9c1185a5c5e9fc54612808977ee8f548b2258d31'],
@@ -230,7 +235,7 @@ describe('Hash', function () {
230
235
  it('realHtonl preserves value when system is big-endian (forced simulation)', () => {
231
236
  // We simulate the big-endian branch of realHtonl by calling
232
237
  // the fallback path directly.
233
- const forceBigEndianRealHtonl = (w: number) => (w >>> 0)
238
+ const forceBigEndianRealHtonl = (w: number): number => (w >>> 0)
234
239
 
235
240
  expect(forceBigEndianRealHtonl(0x11223344)).toBe(0x11223344)
236
241
  expect(forceBigEndianRealHtonl(0xaabbccdd)).toBe(0xaabbccdd)
@@ -512,7 +512,7 @@ describe('Spend', () => {
512
512
  })
513
513
  })
514
514
 
515
- it('Successfully validates a spend where sequence is set to undefined', async () => {
515
+ it('Rejects spending an immature coinbase transaction', async () => {
516
516
  const sourceTransaction = new Transaction(
517
517
  1,
518
518
  [{
@@ -531,8 +531,49 @@ describe('Spend', () => {
531
531
  )
532
532
  const txid = sourceTransaction.id('hex')
533
533
  sourceTransaction.merklePath = MerklePath.fromCoinbaseTxidAndHeight(txid, 0)
534
- const chain = new MockChain({ blockheaders: [] })
535
- chain.addBlock(txid)
534
+ const chain = new MockChain({ blockheaders: [txid] })
535
+
536
+ const spendTx = new Transaction(
537
+ 1,
538
+ [
539
+ {
540
+ unlockingScript: Script.fromASM('OP_TRUE'),
541
+ sourceTransaction,
542
+ sourceOutputIndex: 0
543
+ }
544
+ ],
545
+ [{
546
+ lockingScript: Script.fromASM('OP_NOP'),
547
+ satoshis: 1
548
+ }],
549
+ 0
550
+ )
551
+
552
+ await expect(spendTx.verify(chain)).rejects.toThrow(
553
+ `Invalid merkle path for transaction ${txid}`
554
+ )
555
+ })
556
+
557
+ it('Successfully validates a mature coinbase spend where sequence is set to undefined', async () => {
558
+ const sourceTransaction = new Transaction(
559
+ 1,
560
+ [{
561
+ sourceTXID: '0000000000000000000000000000000000000000000000000000000000000000',
562
+ sourceOutputIndex: 0,
563
+ unlockingScript: Script.fromASM('OP_TRUE'),
564
+ sequence: 0xffffffff
565
+ }],
566
+ [
567
+ {
568
+ lockingScript: Script.fromASM('OP_NOP'),
569
+ satoshis: 2
570
+ }
571
+ ],
572
+ 0
573
+ )
574
+ const txid = sourceTransaction.id('hex')
575
+ sourceTransaction.merklePath = MerklePath.fromCoinbaseTxidAndHeight(txid, 0)
576
+ const chain = new MockChain({ blockheaders: [txid, ...new Array(100).fill('')] })
536
577
 
537
578
  const spendTx = new Transaction(
538
579
  1,
@@ -551,7 +592,7 @@ describe('Spend', () => {
551
592
  )
552
593
 
553
594
  const valid = await spendTx.verify(chain)
554
-
595
+
555
596
  expect(valid).toBe(true)
556
597
 
557
598
  const b = spendTx.toBinary()
@@ -375,7 +375,7 @@ export default class MerklePath {
375
375
  if (this.indexOf(txid) === 0) {
376
376
  // Coinbase transaction outputs can only be spent once they're 100 blocks deep.
377
377
  const height = await chainTracker.currentHeight()
378
- if (this.blockHeight + 100 < height) {
378
+ if (this.blockHeight + 100 > height) {
379
379
  return false
380
380
  }
381
381
  }
@@ -15,6 +15,23 @@ import MerklePath from '../../transaction/MerklePath'
15
15
  import { BEEF_V1 } from '../../transaction/Beef'
16
16
  import SatoshisPerKilobyte from '../../transaction/fee-models/SatoshisPerKilobyte'
17
17
 
18
+ // Default Transaction.fee() resolves to LivePolicy.getInstance(), which fetches the live ARC
19
+ // policy endpoint. Replace it with a deterministic 100 sat/kb model so tests never hit the
20
+ // network — the live endpoint is unreliable and not part of what these tests exercise.
21
+ jest.mock('../../transaction/fee-models/LivePolicy', () => {
22
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
23
+ const SPKB = require('../../transaction/fee-models/SatoshisPerKilobyte').default
24
+ class MockLivePolicy extends SPKB {
25
+ static instance: MockLivePolicy | null = null
26
+ constructor () { super(100) }
27
+ static getInstance (): MockLivePolicy {
28
+ if (!MockLivePolicy.instance) MockLivePolicy.instance = new MockLivePolicy()
29
+ return MockLivePolicy.instance
30
+ }
31
+ }
32
+ return { __esModule: true, default: MockLivePolicy }
33
+ })
34
+
18
35
  import sighashVectors from '../../primitives/__tests/sighash.vectors'
19
36
  import invalidTransactions from './tx.invalid.vectors'
20
37
  import validTransactions from './tx.valid.vectors'
@@ -412,8 +412,23 @@ export interface AbortActionArgs {
412
412
  reference: Base64String
413
413
  }
414
414
 
415
+ /**
416
+ * Result of an `abortAction` call.
417
+ *
418
+ * `aborted` is informative: `true` indicates the wallet successfully invalidated
419
+ * the action (it will not be broadcast and its inputs are released), `false`
420
+ * indicates the wallet refused to abort because the underlying transaction was
421
+ * found to already be on chain (mined or known to mempool). On a refusal the
422
+ * caller should typically invoke `internalizeAction` instead, which will treat
423
+ * the call as explicit authorization to advance the nosend lifecycle.
424
+ *
425
+ * Note that confirming on-chain status requires network reachability. When
426
+ * confirmation is impossible (services unreachable or returning errors), the
427
+ * wallet proceeds with the abort and returns `aborted: true` rather than
428
+ * refusing — refusal is reserved for positive on-chain confirmation.
429
+ */
415
430
  export interface AbortActionResult {
416
- aborted: true
431
+ aborted: boolean
417
432
  }
418
433
 
419
434
  export type AcquireCertificateResult = WalletCertificate
@@ -169,7 +169,7 @@ export default class WalletClient implements WalletInterface {
169
169
 
170
170
  async abortAction (args: {
171
171
  reference: Base64String
172
- }): Promise<{ aborted: true }> {
172
+ }): Promise<{ aborted: boolean }> {
173
173
  validateAbortActionArgs(args)
174
174
  await this.connectToSubstrate()
175
175
  return await (this.substrate as WalletInterface).abortAction(
@@ -130,7 +130,7 @@ export default class WindowCWISubstrate implements WalletInterface {
130
130
  async abortAction(
131
131
  args: { reference: Base64String },
132
132
  originator?: OriginatorDomainNameStringUnder250Bytes
133
- ): Promise<{ aborted: true }> {
133
+ ): Promise<{ aborted: boolean }> {
134
134
  return await this.CWI.abortAction(args, originator)
135
135
  }
136
136