@bsv/sdk 1.10.2 → 1.10.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 +1 -1
- package/dist/cjs/src/auth/Peer.js +51 -1
- package/dist/cjs/src/auth/Peer.js.map +1 -1
- package/dist/cjs/src/auth/clients/AuthFetch.js +13 -3
- package/dist/cjs/src/auth/clients/AuthFetch.js.map +1 -1
- package/dist/cjs/src/primitives/ReaderUint8Array.js +180 -0
- package/dist/cjs/src/primitives/ReaderUint8Array.js.map +1 -0
- package/dist/cjs/src/primitives/WriterUint8Array.js +173 -0
- package/dist/cjs/src/primitives/WriterUint8Array.js.map +1 -0
- package/dist/cjs/src/primitives/utils.js +20 -2
- package/dist/cjs/src/primitives/utils.js.map +1 -1
- package/dist/cjs/src/script/templates/PushDrop.js +21 -7
- package/dist/cjs/src/script/templates/PushDrop.js.map +1 -1
- package/dist/cjs/src/transaction/Beef.js +85 -27
- package/dist/cjs/src/transaction/Beef.js.map +1 -1
- package/dist/cjs/src/transaction/BeefTx.js +32 -14
- package/dist/cjs/src/transaction/BeefTx.js.map +1 -1
- package/dist/cjs/src/transaction/MerklePath.js +25 -6
- package/dist/cjs/src/transaction/MerklePath.js.map +1 -1
- package/dist/cjs/src/transaction/Transaction.js +77 -26
- package/dist/cjs/src/transaction/Transaction.js.map +1 -1
- package/dist/cjs/src/transaction/broadcasters/ARC.js +23 -0
- package/dist/cjs/src/transaction/broadcasters/ARC.js.map +1 -1
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/esm/src/auth/Peer.js +51 -1
- package/dist/esm/src/auth/Peer.js.map +1 -1
- package/dist/esm/src/auth/clients/AuthFetch.js +13 -3
- package/dist/esm/src/auth/clients/AuthFetch.js.map +1 -1
- package/dist/esm/src/primitives/ReaderUint8Array.js +176 -0
- package/dist/esm/src/primitives/ReaderUint8Array.js.map +1 -0
- package/dist/esm/src/primitives/WriterUint8Array.js +169 -0
- package/dist/esm/src/primitives/WriterUint8Array.js.map +1 -0
- package/dist/esm/src/primitives/utils.js +18 -1
- package/dist/esm/src/primitives/utils.js.map +1 -1
- package/dist/esm/src/script/templates/PushDrop.js +21 -7
- package/dist/esm/src/script/templates/PushDrop.js.map +1 -1
- package/dist/esm/src/transaction/Beef.js +86 -28
- package/dist/esm/src/transaction/Beef.js.map +1 -1
- package/dist/esm/src/transaction/BeefTx.js +33 -15
- package/dist/esm/src/transaction/BeefTx.js.map +1 -1
- package/dist/esm/src/transaction/MerklePath.js +26 -7
- package/dist/esm/src/transaction/MerklePath.js.map +1 -1
- package/dist/esm/src/transaction/Transaction.js +78 -27
- package/dist/esm/src/transaction/Transaction.js.map +1 -1
- package/dist/esm/src/transaction/broadcasters/ARC.js +23 -0
- package/dist/esm/src/transaction/broadcasters/ARC.js.map +1 -1
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/dist/types/src/auth/Peer.d.ts +9 -0
- package/dist/types/src/auth/Peer.d.ts.map +1 -1
- package/dist/types/src/auth/clients/AuthFetch.d.ts.map +1 -1
- package/dist/types/src/primitives/ReaderUint8Array.d.ts +32 -0
- package/dist/types/src/primitives/ReaderUint8Array.d.ts.map +1 -0
- package/dist/types/src/primitives/WriterUint8Array.d.ts +54 -0
- package/dist/types/src/primitives/WriterUint8Array.d.ts.map +1 -0
- package/dist/types/src/primitives/utils.d.ts +15 -3
- package/dist/types/src/primitives/utils.d.ts.map +1 -1
- package/dist/types/src/script/templates/PushDrop.d.ts +3 -4
- package/dist/types/src/script/templates/PushDrop.d.ts.map +1 -1
- package/dist/types/src/transaction/Beef.d.ts +24 -7
- package/dist/types/src/transaction/Beef.d.ts.map +1 -1
- package/dist/types/src/transaction/BeefTx.d.ts +13 -6
- package/dist/types/src/transaction/BeefTx.d.ts.map +1 -1
- package/dist/types/src/transaction/MerklePath.d.ts +16 -3
- package/dist/types/src/transaction/MerklePath.d.ts.map +1 -1
- package/dist/types/src/transaction/Transaction.d.ts +44 -7
- package/dist/types/src/transaction/Transaction.d.ts.map +1 -1
- package/dist/types/src/transaction/broadcasters/ARC.d.ts.map +1 -1
- package/dist/types/tsconfig.types.tsbuildinfo +1 -1
- package/dist/umd/bundle.js +3 -3
- package/dist/umd/bundle.js.map +1 -1
- package/docs/reference/primitives.md +167 -29
- package/docs/reference/script.md +1 -1
- package/docs/reference/transaction.md +177 -34
- package/package.json +1 -1
- package/src/auth/Peer.ts +65 -5
- package/src/auth/__tests/Peer.test.ts +8 -2
- package/src/auth/clients/AuthFetch.ts +18 -3
- package/src/overlay-tools/__tests/SHIPBroadcaster.test.ts +9 -0
- package/src/primitives/ReaderUint8Array.ts +196 -0
- package/src/primitives/WriterUint8Array.ts +195 -0
- package/src/primitives/__tests/ReaderUint8Array.test.ts +317 -0
- package/src/primitives/__tests/WriterUint8Array.test.ts +208 -0
- package/src/primitives/utils.ts +20 -2
- package/src/script/templates/PushDrop.ts +32 -17
- package/src/script/templates/__tests/PushDrop.test.ts +28 -0
- package/src/storage/__tests/StorageUploader.test.ts +1 -1
- package/src/transaction/Beef.ts +103 -40
- package/src/transaction/BeefTx.ts +38 -19
- package/src/transaction/MerklePath.ts +30 -9
- package/src/transaction/Transaction.ts +91 -38
- package/src/transaction/__tests/Beef.test.ts +75 -0
- package/src/transaction/broadcasters/ARC.ts +34 -7
- package/src/transaction/broadcasters/__tests/ARC.test.ts +98 -0
package/package.json
CHANGED
package/src/auth/Peer.ts
CHANGED
|
@@ -55,6 +55,12 @@ export class Peer {
|
|
|
55
55
|
{ callback: (sessionNonce: string) => void, sessionNonce: string }
|
|
56
56
|
> = new Map()
|
|
57
57
|
|
|
58
|
+
// Promise-based mechanism for waiting on certificate validation
|
|
59
|
+
private readonly certificateValidationPromises: Map<
|
|
60
|
+
string,
|
|
61
|
+
{ resolve: () => void, reject: (error: Error) => void }
|
|
62
|
+
> = new Map()
|
|
63
|
+
|
|
58
64
|
// Single shared counter for all callback types
|
|
59
65
|
private callbackIdCounter: number = 0
|
|
60
66
|
|
|
@@ -644,6 +650,11 @@ export class Peer {
|
|
|
644
650
|
peerSession.lastUpdate = Date.now()
|
|
645
651
|
this.sessionManager.updateSession(peerSession)
|
|
646
652
|
|
|
653
|
+
// Resolve any promises waiting for certificate validation
|
|
654
|
+
if (peerSession.sessionNonce != null) {
|
|
655
|
+
this.resolveCertificateValidation(peerSession.sessionNonce)
|
|
656
|
+
}
|
|
657
|
+
|
|
647
658
|
this.onCertificatesReceivedCallbacks.forEach(cb =>
|
|
648
659
|
cb(message.identityKey, message.certificates as VerifiableCertificate[])
|
|
649
660
|
)
|
|
@@ -834,6 +845,11 @@ export class Peer {
|
|
|
834
845
|
peerSession.lastUpdate = Date.now()
|
|
835
846
|
this.sessionManager.updateSession(peerSession)
|
|
836
847
|
|
|
848
|
+
// Resolve any promises waiting for certificate validation
|
|
849
|
+
if (peerSession.sessionNonce != null) {
|
|
850
|
+
this.resolveCertificateValidation(peerSession.sessionNonce)
|
|
851
|
+
}
|
|
852
|
+
|
|
837
853
|
// Notify any listeners
|
|
838
854
|
this.onCertificatesReceivedCallbacks.forEach(cb => {
|
|
839
855
|
cb(message.identityKey, message.certificates ?? [])
|
|
@@ -869,12 +885,41 @@ export class Peer {
|
|
|
869
885
|
const certificatesRequired = peerSession.certificatesRequired === true
|
|
870
886
|
const certificatesValidated = peerSession.certificatesValidated === true
|
|
871
887
|
|
|
888
|
+
// If certificates are required but not yet validated, wait for them with a timeout
|
|
872
889
|
if (certificatesRequired && !certificatesValidated) {
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
890
|
+
const CERTIFICATE_WAIT_TIMEOUT_MS = 30000
|
|
891
|
+
const sessionNonce = peerSession.sessionNonce
|
|
892
|
+
|
|
893
|
+
if (sessionNonce == null) {
|
|
894
|
+
throw new Error('Session nonce is required for certificate validation')
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
await new Promise<void>((resolve, reject) => {
|
|
898
|
+
// Set timeout to reject if certificates don't arrive
|
|
899
|
+
const timeoutId = setTimeout(() => {
|
|
900
|
+
const promise = this.certificateValidationPromises.get(sessionNonce)
|
|
901
|
+
if (promise != null) {
|
|
902
|
+
this.certificateValidationPromises.delete(sessionNonce)
|
|
903
|
+
reject(new Error(
|
|
904
|
+
`Timeout waiting for certificate validation from peer ${
|
|
905
|
+
peerSession.peerIdentityKey ?? 'unknown'
|
|
906
|
+
}`
|
|
907
|
+
))
|
|
908
|
+
}
|
|
909
|
+
}, CERTIFICATE_WAIT_TIMEOUT_MS)
|
|
910
|
+
|
|
911
|
+
// Store the promise resolvers with timeout cleanup
|
|
912
|
+
this.certificateValidationPromises.set(sessionNonce, {
|
|
913
|
+
resolve: () => {
|
|
914
|
+
clearTimeout(timeoutId)
|
|
915
|
+
resolve()
|
|
916
|
+
},
|
|
917
|
+
reject: (error: Error) => {
|
|
918
|
+
clearTimeout(timeoutId)
|
|
919
|
+
reject(error)
|
|
920
|
+
}
|
|
921
|
+
})
|
|
922
|
+
})
|
|
878
923
|
}
|
|
879
924
|
|
|
880
925
|
const { valid } = await this.wallet.verifySignature({
|
|
@@ -904,6 +949,21 @@ export class Peer {
|
|
|
904
949
|
})
|
|
905
950
|
}
|
|
906
951
|
|
|
952
|
+
/**
|
|
953
|
+
* Resolves any pending certificate validation promises for the given session nonce.
|
|
954
|
+
* This should be called when certificates have been successfully validated.
|
|
955
|
+
*
|
|
956
|
+
* @private
|
|
957
|
+
* @param {string} sessionNonce - The session nonce to resolve promises for.
|
|
958
|
+
*/
|
|
959
|
+
private resolveCertificateValidation (sessionNonce: string): void {
|
|
960
|
+
const promise = this.certificateValidationPromises.get(sessionNonce)
|
|
961
|
+
if (promise != null) {
|
|
962
|
+
promise.resolve()
|
|
963
|
+
this.certificateValidationPromises.delete(sessionNonce)
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
|
|
907
967
|
private async getIdentityPublicKey (): Promise<string> {
|
|
908
968
|
if (this.identityPublicKey != null) {
|
|
909
969
|
return this.identityPublicKey
|
|
@@ -894,12 +894,18 @@ describe('Peer class mutual authentication and certificate exchange', () => {
|
|
|
894
894
|
|
|
895
895
|
setupPeers(false, true) // Bob requires certs
|
|
896
896
|
|
|
897
|
+
// Prevent Alice from auto-sending certificate response
|
|
898
|
+
// This keeps Bob in "certs required but not validated" state
|
|
899
|
+
alice.listenForCertificatesRequested(() => {
|
|
900
|
+
// Intentionally do nothing (no auto-response)
|
|
901
|
+
})
|
|
902
|
+
|
|
897
903
|
let received = false
|
|
898
904
|
bob.listenForGeneralMessages(() => {
|
|
899
905
|
received = true
|
|
900
906
|
})
|
|
901
907
|
|
|
902
|
-
// Send message —
|
|
908
|
+
// Send message — Bob will wait for certificates that never arrive
|
|
903
909
|
try {
|
|
904
910
|
await alice.toPeer(Utils.toArray('Hello Bob!'), bobPubKey)
|
|
905
911
|
} catch {
|
|
@@ -909,7 +915,7 @@ describe('Peer class mutual authentication and certificate exchange', () => {
|
|
|
909
915
|
// Allow transport handlers to run
|
|
910
916
|
await new Promise(r => setTimeout(r, 50))
|
|
911
917
|
|
|
912
|
-
// Message must NOT be delivered
|
|
918
|
+
// Message must NOT be delivered since certificates haven't been validated
|
|
913
919
|
expect(received).toBe(false)
|
|
914
920
|
})
|
|
915
921
|
|
|
@@ -237,12 +237,27 @@ export class AuthFetch {
|
|
|
237
237
|
// If the server has a resource that requires certificates to be sent before access would be granted,
|
|
238
238
|
// this makes sure the user has a chance to send the certificates before the resource is requested.
|
|
239
239
|
if (peerToUse.pendingCertificateRequests.length > 0) {
|
|
240
|
-
|
|
241
|
-
|
|
240
|
+
const CERTIFICATE_WAIT_TIMEOUT_MS = 30000
|
|
241
|
+
const CHECK_INTERVAL_MS = 100
|
|
242
|
+
|
|
243
|
+
await new Promise<void>((resolve, reject) => {
|
|
244
|
+
const startTime = Date.now()
|
|
245
|
+
|
|
246
|
+
const checkPending = (): void => {
|
|
242
247
|
if (peerToUse.pendingCertificateRequests.length === 0) {
|
|
243
248
|
resolve()
|
|
249
|
+
return
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
if (Date.now() - startTime > CERTIFICATE_WAIT_TIMEOUT_MS) {
|
|
253
|
+
reject(new Error('Timeout waiting for certificate request to complete'))
|
|
254
|
+
return
|
|
244
255
|
}
|
|
245
|
-
|
|
256
|
+
|
|
257
|
+
setTimeout(checkPending, CHECK_INTERVAL_MS)
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
checkPending()
|
|
246
261
|
})
|
|
247
262
|
}
|
|
248
263
|
|
|
@@ -339,8 +339,12 @@ describe('SHIPCast', () => {
|
|
|
339
339
|
})
|
|
340
340
|
const testTx = new Transaction(1, [], [], 0)
|
|
341
341
|
|
|
342
|
+
const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => { })
|
|
343
|
+
|
|
342
344
|
const result = await b.broadcast(testTx)
|
|
343
345
|
|
|
346
|
+
expect(consoleErrorSpy).toHaveBeenCalled()
|
|
347
|
+
|
|
344
348
|
expect(result).toEqual({
|
|
345
349
|
status: 'error',
|
|
346
350
|
code: 'ERR_ALL_HOSTS_REJECTED',
|
|
@@ -1076,8 +1080,13 @@ describe('SHIPCast', () => {
|
|
|
1076
1080
|
resolver: mockResolver as unknown as LookupResolver
|
|
1077
1081
|
})
|
|
1078
1082
|
const testTx = new Transaction(1, [], [], 0)
|
|
1083
|
+
|
|
1084
|
+
const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => { })
|
|
1085
|
+
|
|
1079
1086
|
const response = await b.broadcast(testTx)
|
|
1080
1087
|
|
|
1088
|
+
expect(consoleErrorSpy).toHaveBeenCalled()
|
|
1089
|
+
|
|
1081
1090
|
// Since the host responded (successfully in terms of HTTP), but with invalid data, we should consider it a failure
|
|
1082
1091
|
expect(response).toEqual({
|
|
1083
1092
|
status: 'error',
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import BigNumber from './BigNumber.js'
|
|
2
|
+
import { Reader } from './utils.js'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Reader for serialized Uint8Array binary data.
|
|
6
|
+
*/
|
|
7
|
+
export class ReaderUint8Array {
|
|
8
|
+
public bin: Uint8Array
|
|
9
|
+
public pos: number
|
|
10
|
+
private readonly length: number
|
|
11
|
+
|
|
12
|
+
static makeReader (bin: Uint8Array | number[], pos: number = 0): Reader | ReaderUint8Array {
|
|
13
|
+
if (bin instanceof Uint8Array) {
|
|
14
|
+
return new ReaderUint8Array(bin, pos)
|
|
15
|
+
}
|
|
16
|
+
if (Array.isArray(bin)) {
|
|
17
|
+
return new Reader(bin, pos)
|
|
18
|
+
}
|
|
19
|
+
throw new Error('ReaderUint8Array.makeReader: bin must be Uint8Array or number[]')
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
constructor (bin: Uint8Array | number[] = new Uint8Array(0), pos: number = 0) {
|
|
23
|
+
if (bin instanceof Uint8Array) {
|
|
24
|
+
this.bin = bin
|
|
25
|
+
} else if (Array.isArray(bin)) {
|
|
26
|
+
this.bin = new Uint8Array(bin)
|
|
27
|
+
} else {
|
|
28
|
+
throw new Error('ReaderUint8Array constructor: bin must be Uint8Array or number[]')
|
|
29
|
+
}
|
|
30
|
+
this.pos = pos
|
|
31
|
+
this.length = this.bin.length
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
public eof (): boolean {
|
|
35
|
+
return this.pos >= this.length
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
public read (len = this.length): Uint8Array {
|
|
39
|
+
const start = this.pos
|
|
40
|
+
const end = this.pos + len
|
|
41
|
+
this.pos = end
|
|
42
|
+
return this.bin.slice(start, end)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
public readReverse (len = this.length): Uint8Array {
|
|
46
|
+
const buf2 = new Uint8Array(len)
|
|
47
|
+
for (let i = 0; i < len; i++) {
|
|
48
|
+
buf2[i] = this.bin[this.pos + len - 1 - i]
|
|
49
|
+
}
|
|
50
|
+
this.pos += len
|
|
51
|
+
return buf2
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
public readUInt8 (): number {
|
|
55
|
+
const val = this.bin[this.pos]
|
|
56
|
+
this.pos += 1
|
|
57
|
+
return val
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
public readInt8 (): number {
|
|
61
|
+
const val = this.bin[this.pos]
|
|
62
|
+
this.pos += 1
|
|
63
|
+
// If the sign bit is set, convert to negative value
|
|
64
|
+
return (val & 0x80) !== 0 ? val - 0x100 : val
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
public readUInt16BE (): number {
|
|
68
|
+
const val = (this.bin[this.pos] << 8) | this.bin[this.pos + 1]
|
|
69
|
+
this.pos += 2
|
|
70
|
+
return val
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
public readInt16BE (): number {
|
|
74
|
+
const val = this.readUInt16BE()
|
|
75
|
+
// If the sign bit is set, convert to negative value
|
|
76
|
+
return (val & 0x8000) !== 0 ? val - 0x10000 : val
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
public readUInt16LE (): number {
|
|
80
|
+
const val = this.bin[this.pos] | (this.bin[this.pos + 1] << 8)
|
|
81
|
+
this.pos += 2
|
|
82
|
+
return val
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
public readInt16LE (): number {
|
|
86
|
+
const val = this.readUInt16LE()
|
|
87
|
+
// If the sign bit is set, convert to negative value
|
|
88
|
+
const x = (val & 0x8000) !== 0 ? val - 0x10000 : val
|
|
89
|
+
return x
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
public readUInt32BE (): number {
|
|
93
|
+
const val =
|
|
94
|
+
this.bin[this.pos] * 0x1000000 + // Shift the first byte by 24 bits
|
|
95
|
+
((this.bin[this.pos + 1] << 16) | // Shift the second byte by 16 bits
|
|
96
|
+
(this.bin[this.pos + 2] << 8) | // Shift the third byte by 8 bits
|
|
97
|
+
this.bin[this.pos + 3]) // The fourth byte
|
|
98
|
+
this.pos += 4
|
|
99
|
+
return val
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
public readInt32BE (): number {
|
|
103
|
+
const val = this.readUInt32BE()
|
|
104
|
+
// If the sign bit is set, convert to negative value
|
|
105
|
+
return (val & 0x80000000) !== 0 ? val - 0x100000000 : val
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
public readUInt32LE (): number {
|
|
109
|
+
const val =
|
|
110
|
+
(this.bin[this.pos] |
|
|
111
|
+
(this.bin[this.pos + 1] << 8) |
|
|
112
|
+
(this.bin[this.pos + 2] << 16) |
|
|
113
|
+
(this.bin[this.pos + 3] << 24)) >>>
|
|
114
|
+
0
|
|
115
|
+
this.pos += 4
|
|
116
|
+
return val
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
public readInt32LE (): number {
|
|
120
|
+
const val = this.readUInt32LE()
|
|
121
|
+
// Explicitly check if the sign bit is set and then convert to a negative value
|
|
122
|
+
return (val & 0x80000000) !== 0 ? val - 0x100000000 : val
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
public readUInt64BEBn (): BigNumber {
|
|
126
|
+
const bin = Array.from(this.bin.slice(this.pos, this.pos + 8))
|
|
127
|
+
const bn = new BigNumber(bin)
|
|
128
|
+
this.pos = this.pos + 8
|
|
129
|
+
return bn
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
public readUInt64LEBn (): BigNumber {
|
|
133
|
+
const bin = Array.from(this.readReverse(8))
|
|
134
|
+
const bn = new BigNumber(bin)
|
|
135
|
+
return bn
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
public readInt64LEBn (): BigNumber {
|
|
139
|
+
const OverflowInt64 = new BigNumber(2).pow(new BigNumber(63))
|
|
140
|
+
const OverflowUint64 = new BigNumber(2).pow(new BigNumber(64))
|
|
141
|
+
const bin = Array.from(this.readReverse(8))
|
|
142
|
+
let bn = new BigNumber(bin)
|
|
143
|
+
if (bn.gte(OverflowInt64)) {
|
|
144
|
+
bn = bn.sub(OverflowUint64) // Adjust for negative numbers
|
|
145
|
+
}
|
|
146
|
+
return bn
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
public readVarIntNum (signed: boolean = true): number {
|
|
150
|
+
const first = this.readUInt8()
|
|
151
|
+
let bn: BigNumber
|
|
152
|
+
switch (first) {
|
|
153
|
+
case 0xfd:
|
|
154
|
+
return this.readUInt16LE()
|
|
155
|
+
case 0xfe:
|
|
156
|
+
return this.readUInt32LE()
|
|
157
|
+
case 0xff:
|
|
158
|
+
bn = signed ? this.readInt64LEBn() : this.readUInt64LEBn()
|
|
159
|
+
if (bn.lte(new BigNumber(2).pow(new BigNumber(53)))) {
|
|
160
|
+
return bn.toNumber()
|
|
161
|
+
} else {
|
|
162
|
+
throw new Error('number too large to retain precision - use readVarIntBn')
|
|
163
|
+
}
|
|
164
|
+
default:
|
|
165
|
+
return first
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
public readVarInt (): Uint8Array {
|
|
170
|
+
const first = this.bin[this.pos]
|
|
171
|
+
switch (first) {
|
|
172
|
+
case 0xfd:
|
|
173
|
+
return this.read(1 + 2)
|
|
174
|
+
case 0xfe:
|
|
175
|
+
return this.read(1 + 4)
|
|
176
|
+
case 0xff:
|
|
177
|
+
return this.read(1 + 8)
|
|
178
|
+
default:
|
|
179
|
+
return this.read(1)
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
public readVarIntBn (): BigNumber {
|
|
184
|
+
const first = this.readUInt8()
|
|
185
|
+
switch (first) {
|
|
186
|
+
case 0xfd:
|
|
187
|
+
return new BigNumber(this.readUInt16LE())
|
|
188
|
+
case 0xfe:
|
|
189
|
+
return new BigNumber(this.readUInt32LE())
|
|
190
|
+
case 0xff:
|
|
191
|
+
return this.readUInt64LEBn()
|
|
192
|
+
default:
|
|
193
|
+
return new BigNumber(first)
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import BigNumber from './BigNumber.js'
|
|
2
|
+
import { Writer } from './utils.js'
|
|
3
|
+
|
|
4
|
+
type WriterChunk = readonly number[] | Uint8Array
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* WriterUint8Array is a utility class for writing binary data into a dynamically
|
|
8
|
+
* growing Uint8Array buffer. It provides methods to write various integer types
|
|
9
|
+
* and variable-length integers, similar to the Writer class but optimized for
|
|
10
|
+
* Uint8Array usage.
|
|
11
|
+
*/
|
|
12
|
+
export class WriterUint8Array {
|
|
13
|
+
private buffer: Uint8Array
|
|
14
|
+
private pos: number
|
|
15
|
+
private capacity: number
|
|
16
|
+
|
|
17
|
+
constructor (bufs?: WriterChunk[], initialCapacity: number = 256) {
|
|
18
|
+
if ((bufs != null) && bufs.length > 0) {
|
|
19
|
+
const totalLength = bufs.reduce((sum, buf) => sum + buf.length, 0)
|
|
20
|
+
initialCapacity = Math.max(initialCapacity, totalLength)
|
|
21
|
+
}
|
|
22
|
+
this.buffer = new Uint8Array(initialCapacity)
|
|
23
|
+
this.pos = 0
|
|
24
|
+
this.capacity = initialCapacity
|
|
25
|
+
if (bufs != null) {
|
|
26
|
+
for (const buf of bufs) {
|
|
27
|
+
this.write(buf)
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Returns the current length of written data
|
|
34
|
+
*/
|
|
35
|
+
getLength (): number {
|
|
36
|
+
return this.pos
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @return the written data as Uint8Array copy of the internal buffer
|
|
41
|
+
*/
|
|
42
|
+
toUint8Array (): Uint8Array {
|
|
43
|
+
return this.buffer.slice(0, this.pos)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Legacy compatibility method – returns number[] (Byte[])
|
|
48
|
+
*/
|
|
49
|
+
toArray (): number[] {
|
|
50
|
+
return Array.from(this.toUint8Array())
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* @return the written data as Uint8Array. CAUTION: This is zero-copy subarray of the internal buffer).
|
|
55
|
+
*/
|
|
56
|
+
toUint8ArrayZeroCopy (): Uint8Array {
|
|
57
|
+
return this.buffer.subarray(0, this.pos)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
private ensureCapacity (needed: number): void {
|
|
61
|
+
if (this.pos + needed > this.capacity) {
|
|
62
|
+
let newCapacity = this.capacity * 2
|
|
63
|
+
while (this.pos + needed > newCapacity) {
|
|
64
|
+
newCapacity *= 2
|
|
65
|
+
}
|
|
66
|
+
const newBuffer = new Uint8Array(newCapacity)
|
|
67
|
+
newBuffer.set(this.buffer)
|
|
68
|
+
this.buffer = newBuffer
|
|
69
|
+
this.capacity = newCapacity
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
write (bytes: WriterChunk): this {
|
|
74
|
+
const data = bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes)
|
|
75
|
+
this.ensureCapacity(data.length)
|
|
76
|
+
this.buffer.set(data, this.pos)
|
|
77
|
+
this.pos += data.length
|
|
78
|
+
return this
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
writeReverse (buf: WriterChunk): this {
|
|
82
|
+
const data = buf instanceof Uint8Array ? buf : new Uint8Array(buf)
|
|
83
|
+
this.ensureCapacity(data.length)
|
|
84
|
+
for (let i = data.length - 1; i >= 0; i--) {
|
|
85
|
+
this.buffer[this.pos] = data[i]
|
|
86
|
+
this.pos += 1
|
|
87
|
+
}
|
|
88
|
+
return this
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
writeUInt8 (value: number): this {
|
|
92
|
+
this.ensureCapacity(1)
|
|
93
|
+
this.buffer[this.pos] = value & 0xff
|
|
94
|
+
this.pos += 1
|
|
95
|
+
return this
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
writeInt8 (value: number): this {
|
|
99
|
+
this.writeUInt8(value)
|
|
100
|
+
return this
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
writeUInt16LE (value: number): this {
|
|
104
|
+
this.ensureCapacity(2)
|
|
105
|
+
this.buffer[this.pos] = value & 0xff
|
|
106
|
+
this.buffer[this.pos + 1] = (value >> 8) & 0xff
|
|
107
|
+
this.pos += 2
|
|
108
|
+
return this
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
writeUInt16BE (value: number): this {
|
|
112
|
+
this.ensureCapacity(2)
|
|
113
|
+
this.buffer[this.pos] = (value >> 8) & 0xff
|
|
114
|
+
this.buffer[this.pos + 1] = value & 0xff
|
|
115
|
+
this.pos += 2
|
|
116
|
+
return this
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
writeInt16LE (value: number): this {
|
|
120
|
+
this.writeUInt16LE(value & 0xffff)
|
|
121
|
+
return this
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
writeInt16BE (value: number): this {
|
|
125
|
+
this.writeUInt16BE(value & 0xffff)
|
|
126
|
+
return this
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
writeUInt32LE (value: number): this {
|
|
130
|
+
this.ensureCapacity(4)
|
|
131
|
+
this.buffer[this.pos] = value & 0xff
|
|
132
|
+
this.buffer[this.pos + 1] = (value >> 8) & 0xff
|
|
133
|
+
this.buffer[this.pos + 2] = (value >> 16) & 0xff
|
|
134
|
+
this.buffer[this.pos + 3] = (value >> 24) & 0xff
|
|
135
|
+
this.pos += 4
|
|
136
|
+
return this
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
writeUInt32BE (value: number): this {
|
|
140
|
+
this.ensureCapacity(4)
|
|
141
|
+
this.buffer[this.pos] = (value >> 24) & 0xff
|
|
142
|
+
this.buffer[this.pos + 1] = (value >> 16) & 0xff
|
|
143
|
+
this.buffer[this.pos + 2] = (value >> 8) & 0xff
|
|
144
|
+
this.buffer[this.pos + 3] = value & 0xff
|
|
145
|
+
this.pos += 4
|
|
146
|
+
return this
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
writeInt32LE (value: number): this {
|
|
150
|
+
this.writeUInt32LE(value >>> 0)
|
|
151
|
+
return this
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
writeInt32BE (value: number): this {
|
|
155
|
+
this.writeUInt32BE(value >>> 0)
|
|
156
|
+
return this
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
writeUInt64BEBn (bn: BigNumber): this {
|
|
160
|
+
const buf = bn.toArray('be', 8)
|
|
161
|
+
this.write(buf)
|
|
162
|
+
return this
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
writeUInt64LEBn (bn: BigNumber): this {
|
|
166
|
+
const buf = bn.toArray('be', 8)
|
|
167
|
+
this.writeReverse(buf)
|
|
168
|
+
return this
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
writeUInt64LE (n: number): this {
|
|
172
|
+
const buf = new BigNumber(n).toArray('be', 8)
|
|
173
|
+
this.writeReverse(buf)
|
|
174
|
+
return this
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
writeVarIntNum (n: number): this {
|
|
178
|
+
const buf = Writer.varIntNum(n)
|
|
179
|
+
this.write(buf)
|
|
180
|
+
return this
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
writeVarIntBn (bn: BigNumber): this {
|
|
184
|
+
const buf = Writer.varIntBn(bn)
|
|
185
|
+
this.write(buf)
|
|
186
|
+
return this
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Resets the writer to empty state (reuses the buffer)
|
|
191
|
+
*/
|
|
192
|
+
reset (): void {
|
|
193
|
+
this.pos = 0
|
|
194
|
+
}
|
|
195
|
+
}
|