@bsv/sdk 2.1.2 → 2.1.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.
- package/dist/cjs/package.json +13 -13
- package/dist/cjs/src/auth/Peer.js +21 -18
- package/dist/cjs/src/auth/Peer.js.map +1 -1
- package/dist/cjs/src/auth/SessionManager.js.map +1 -1
- package/dist/cjs/src/auth/clients/AuthFetch.js +4 -1
- package/dist/cjs/src/auth/clients/AuthFetch.js.map +1 -1
- package/dist/cjs/src/compat/Mnemonic.js +12 -0
- package/dist/cjs/src/compat/Mnemonic.js.map +1 -1
- package/dist/cjs/src/overlay-tools/LookupResolver.js +99 -28
- package/dist/cjs/src/overlay-tools/LookupResolver.js.map +1 -1
- package/dist/cjs/src/transaction/MerklePath.js +1 -1
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/esm/src/auth/Peer.js +28 -18
- package/dist/esm/src/auth/Peer.js.map +1 -1
- package/dist/esm/src/auth/SessionManager.js.map +1 -1
- package/dist/esm/src/auth/clients/AuthFetch.js +4 -1
- package/dist/esm/src/auth/clients/AuthFetch.js.map +1 -1
- package/dist/esm/src/compat/Mnemonic.js +12 -0
- package/dist/esm/src/compat/Mnemonic.js.map +1 -1
- package/dist/esm/src/overlay-tools/LookupResolver.js +99 -28
- package/dist/esm/src/overlay-tools/LookupResolver.js.map +1 -1
- package/dist/esm/src/transaction/MerklePath.js +1 -1
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/dist/types/src/auth/Peer.d.ts +3 -3
- package/dist/types/src/auth/Peer.d.ts.map +1 -1
- package/dist/types/src/auth/SessionManager.d.ts +21 -0
- package/dist/types/src/auth/SessionManager.d.ts.map +1 -1
- package/dist/types/src/auth/clients/AuthFetch.d.ts +2 -2
- package/dist/types/src/auth/clients/AuthFetch.d.ts.map +1 -1
- package/dist/types/src/compat/Mnemonic.d.ts +2 -0
- package/dist/types/src/compat/Mnemonic.d.ts.map +1 -1
- package/dist/types/src/overlay-tools/LookupResolver.d.ts +1 -0
- package/dist/types/src/overlay-tools/LookupResolver.d.ts.map +1 -1
- package/dist/types/tsconfig.types.tsbuildinfo +1 -1
- package/dist/umd/bundle.js +3 -3
- package/package.json +13 -13
- package/src/auth/Peer.ts +30 -20
- package/src/auth/SessionManager.ts +22 -0
- package/src/auth/__tests/Peer.test.ts +47 -1
- package/src/auth/clients/AuthFetch.ts +6 -3
- package/src/compat/Mnemonic.ts +13 -0
- package/src/compat/__tests/Mnemonic.test.ts +49 -5
- package/src/overlay-tools/LookupResolver.ts +102 -25
- package/src/overlay-tools/__tests/LookupResolver.additional.test.ts +90 -0
- package/src/script/__tests/Spend.test.ts +45 -4
- package/src/transaction/MerklePath.ts +1 -1
- package/src/transaction/__tests/Transaction.test.ts +17 -0
|
@@ -472,6 +472,22 @@ describe('LookupResolver – additional coverage', () => {
|
|
|
472
472
|
).rejects.toThrow('Request timed out')
|
|
473
473
|
})
|
|
474
474
|
|
|
475
|
+
it('rejects within the timeout even when fetch never settles', async () => {
|
|
476
|
+
// Simulate the CORS-blocked / hung-preflight case where the fetch promise
|
|
477
|
+
// does not honor the AbortController signal and never settles.
|
|
478
|
+
const neverFetch = jest.fn().mockImplementation(
|
|
479
|
+
() => new Promise(() => { /* never resolves */ })
|
|
480
|
+
)
|
|
481
|
+
const facilitator = new HTTPSOverlayLookupFacilitator(neverFetch, true)
|
|
482
|
+
const start = Date.now()
|
|
483
|
+
await expect(
|
|
484
|
+
facilitator.lookup('http://host', { service: 'ls_test', query: {} }, 50)
|
|
485
|
+
).rejects.toThrow('Request timed out')
|
|
486
|
+
const elapsed = Date.now() - start
|
|
487
|
+
// Allow generous slack but must complete well before any global jest timeout.
|
|
488
|
+
expect(elapsed).toBeLessThan(2000)
|
|
489
|
+
})
|
|
490
|
+
|
|
475
491
|
it('parses octet-stream responses', async () => {
|
|
476
492
|
// Build a minimal octet-stream payload: 1 outpoint, then BEEF bytes
|
|
477
493
|
const tx = new Transaction(
|
|
@@ -506,6 +522,36 @@ describe('LookupResolver – additional coverage', () => {
|
|
|
506
522
|
expect(result.outputs[0].outputIndex).toBe(0)
|
|
507
523
|
})
|
|
508
524
|
|
|
525
|
+
it('parses octet-stream responses when header carries parameters or differing case', async () => {
|
|
526
|
+
const tx = new Transaction(
|
|
527
|
+
1,
|
|
528
|
+
[],
|
|
529
|
+
[{ lockingScript: LockingScript.fromHex('88'), satoshis: 1 }],
|
|
530
|
+
0
|
|
531
|
+
)
|
|
532
|
+
const beef = tx.toBEEF()
|
|
533
|
+
const txid = Buffer.from(tx.id('hex'), 'hex')
|
|
534
|
+
const payload = Buffer.concat([
|
|
535
|
+
Buffer.from([0x01]), txid, Buffer.from([0x00]), Buffer.from([0x00]), Buffer.from(beef)
|
|
536
|
+
])
|
|
537
|
+
|
|
538
|
+
for (const header of [
|
|
539
|
+
'application/octet-stream; charset=utf-8',
|
|
540
|
+
'Application/Octet-Stream',
|
|
541
|
+
' application/octet-stream '
|
|
542
|
+
]) {
|
|
543
|
+
const mockFetch = jest.fn().mockResolvedValue({
|
|
544
|
+
ok: true,
|
|
545
|
+
headers: { get: () => header },
|
|
546
|
+
arrayBuffer: async () => payload.buffer.slice(payload.byteOffset, payload.byteOffset + payload.byteLength)
|
|
547
|
+
})
|
|
548
|
+
const facilitator = new HTTPSOverlayLookupFacilitator(mockFetch, true)
|
|
549
|
+
const result = await facilitator.lookup('https://host', { service: 'ls_test', query: {} })
|
|
550
|
+
expect(result.type).toBe('output-list')
|
|
551
|
+
expect(result.outputs).toHaveLength(1)
|
|
552
|
+
}
|
|
553
|
+
})
|
|
554
|
+
|
|
509
555
|
it('parses octet-stream responses with context bytes', async () => {
|
|
510
556
|
const tx = new Transaction(
|
|
511
557
|
1,
|
|
@@ -547,6 +593,50 @@ describe('LookupResolver – additional coverage', () => {
|
|
|
547
593
|
).rejects.toThrow('DNS failure')
|
|
548
594
|
})
|
|
549
595
|
|
|
596
|
+
it('normalises string thrown values from fetch', async () => {
|
|
597
|
+
const mockFetch = jest.fn().mockRejectedValue('boom')
|
|
598
|
+
const facilitator = new HTTPSOverlayLookupFacilitator(mockFetch, true)
|
|
599
|
+
await expect(
|
|
600
|
+
facilitator.lookup('https://host', { service: 'ls_test', query: {} })
|
|
601
|
+
).rejects.toThrow('boom')
|
|
602
|
+
})
|
|
603
|
+
|
|
604
|
+
it('normalises object-with-message thrown values from fetch', async () => {
|
|
605
|
+
const mockFetch = jest.fn().mockRejectedValue({ message: 'object boom' })
|
|
606
|
+
const facilitator = new HTTPSOverlayLookupFacilitator(mockFetch, true)
|
|
607
|
+
await expect(
|
|
608
|
+
facilitator.lookup('https://host', { service: 'ls_test', query: {} })
|
|
609
|
+
).rejects.toThrow('object boom')
|
|
610
|
+
})
|
|
611
|
+
|
|
612
|
+
it('normalises plain-object thrown values via JSON', async () => {
|
|
613
|
+
const mockFetch = jest.fn().mockRejectedValue({ code: 42 })
|
|
614
|
+
const facilitator = new HTTPSOverlayLookupFacilitator(mockFetch, true)
|
|
615
|
+
await expect(
|
|
616
|
+
facilitator.lookup('https://host', { service: 'ls_test', query: {} })
|
|
617
|
+
).rejects.toThrow('{"code":42}')
|
|
618
|
+
})
|
|
619
|
+
|
|
620
|
+
it('normalises number/boolean/null thrown values from fetch', async () => {
|
|
621
|
+
for (const value of [123, true, null]) {
|
|
622
|
+
const mockFetch = jest.fn().mockRejectedValue(value)
|
|
623
|
+
const facilitator = new HTTPSOverlayLookupFacilitator(mockFetch, true)
|
|
624
|
+
await expect(
|
|
625
|
+
facilitator.lookup('https://host', { service: 'ls_test', query: {} })
|
|
626
|
+
).rejects.toThrow(String(value))
|
|
627
|
+
}
|
|
628
|
+
})
|
|
629
|
+
|
|
630
|
+
it('normalises circular thrown values without crashing', async () => {
|
|
631
|
+
const circular: { self?: unknown } = {}
|
|
632
|
+
circular.self = circular
|
|
633
|
+
const mockFetch = jest.fn().mockRejectedValue(circular)
|
|
634
|
+
const facilitator = new HTTPSOverlayLookupFacilitator(mockFetch, true)
|
|
635
|
+
await expect(
|
|
636
|
+
facilitator.lookup('https://host', { service: 'ls_test', query: {} })
|
|
637
|
+
).rejects.toThrow('Unknown error')
|
|
638
|
+
})
|
|
639
|
+
|
|
550
640
|
it('sends correct request body to /lookup endpoint', async () => {
|
|
551
641
|
const mockFetch = jest.fn().mockResolvedValue({
|
|
552
642
|
ok: true,
|
|
@@ -512,7 +512,7 @@ describe('Spend', () => {
|
|
|
512
512
|
})
|
|
513
513
|
})
|
|
514
514
|
|
|
515
|
-
it('
|
|
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
|
-
|
|
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
|
|
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'
|