@bcts/dcbor 1.0.0-alpha.8 → 1.0.0-beta.0
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/LICENSE +3 -2
- package/README.md +1 -1
- package/dist/index.cjs +1941 -1497
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +613 -327
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +613 -327
- package/dist/index.d.mts.map +1 -1
- package/dist/index.iife.js +2149 -1754
- package/dist/index.iife.js.map +1 -1
- package/dist/index.mjs +1932 -1511
- package/dist/index.mjs.map +1 -1
- package/package.json +14 -16
- package/src/bignum.ts +347 -0
- package/src/byte-string.ts +21 -17
- package/src/cbor-codable.ts +4 -0
- package/src/cbor-tagged-codable.ts +4 -0
- package/src/cbor-tagged-decodable.ts +33 -5
- package/src/cbor-tagged-encodable.ts +15 -0
- package/src/cbor-tagged.ts +11 -1
- package/src/cbor.ts +73 -18
- package/src/conveniences.ts +23 -5
- package/src/date.ts +35 -28
- package/src/decode.ts +13 -0
- package/src/diag.ts +233 -196
- package/src/dump.ts +18 -7
- package/src/error.ts +4 -0
- package/src/exact.ts +4 -0
- package/src/float.ts +25 -6
- package/src/global.d.ts +4 -0
- package/src/globals.d.ts +5 -0
- package/src/index.ts +65 -13
- package/src/map.ts +27 -23
- package/src/prelude.ts +12 -2
- package/src/set.ts +37 -10
- package/src/simple.ts +15 -2
- package/src/sortable.ts +70 -0
- package/src/stdlib.ts +4 -0
- package/src/string-util.ts +4 -0
- package/src/tag.ts +51 -2
- package/src/tags-store.ts +53 -68
- package/src/tags.ts +68 -6
- package/src/varint.ts +16 -11
- package/src/walk.ts +77 -281
package/package.json
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bcts/dcbor",
|
|
3
|
-
"version": "1.0.0-
|
|
3
|
+
"version": "1.0.0-beta.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Blockchain Commons Deterministic CBOR (dCBOR) for TypeScript",
|
|
6
6
|
"license": "BSD-2-Clause-Patent",
|
|
7
|
-
"author": "
|
|
8
|
-
"homepage": "https://
|
|
7
|
+
"author": "Parity Technologies <admin@parity.io> (https://parity.io)",
|
|
8
|
+
"homepage": "https://bcts.dev",
|
|
9
9
|
"repository": {
|
|
10
10
|
"type": "git",
|
|
11
|
-
"url": "https://github.com/
|
|
11
|
+
"url": "https://github.com/paritytech/bcts",
|
|
12
12
|
"directory": "packages/dcbor"
|
|
13
13
|
},
|
|
14
14
|
"bugs": {
|
|
15
|
-
"url": "https://github.com/
|
|
15
|
+
"url": "https://github.com/paritytech/bcts/issues"
|
|
16
16
|
},
|
|
17
17
|
"main": "dist/index.cjs",
|
|
18
18
|
"module": "dist/index.mjs",
|
|
@@ -33,11 +33,8 @@
|
|
|
33
33
|
],
|
|
34
34
|
"scripts": {
|
|
35
35
|
"build": "tsdown",
|
|
36
|
-
"dev": "tsdown --watch",
|
|
37
36
|
"test": "vitest run",
|
|
38
37
|
"test:watch": "vitest",
|
|
39
|
-
"test:cli": "vitest run tests/cli.test.ts",
|
|
40
|
-
"test:examples": "npm run build && node scripts/test-examples.js",
|
|
41
38
|
"lint": "eslint 'src/**/*.ts' 'tests/**/*.ts'",
|
|
42
39
|
"lint:fix": "eslint 'src/**/*.ts' 'tests/**/*.ts' --fix",
|
|
43
40
|
"typecheck": "tsc --noEmit",
|
|
@@ -61,16 +58,17 @@
|
|
|
61
58
|
"devDependencies": {
|
|
62
59
|
"@bcts/eslint": "^0.1.0",
|
|
63
60
|
"@bcts/tsconfig": "^0.1.0",
|
|
64
|
-
"@eslint/js": "^
|
|
61
|
+
"@eslint/js": "^10.0.1",
|
|
65
62
|
"@types/collections": "^5.1.5",
|
|
66
|
-
"@
|
|
67
|
-
"@typescript-eslint/
|
|
68
|
-
"eslint": "^
|
|
63
|
+
"@types/node": "^25.6.0",
|
|
64
|
+
"@typescript-eslint/eslint-plugin": "^8.59.0",
|
|
65
|
+
"@typescript-eslint/parser": "^8.59.0",
|
|
66
|
+
"eslint": "^10.2.1",
|
|
69
67
|
"ts-node": "^10.9.2",
|
|
70
|
-
"tsdown": "^0.
|
|
71
|
-
"typedoc": "^0.28.
|
|
72
|
-
"typescript": "^
|
|
73
|
-
"vitest": "^
|
|
68
|
+
"tsdown": "^0.21.0",
|
|
69
|
+
"typedoc": "^0.28.19",
|
|
70
|
+
"typescript": "^6.0.3",
|
|
71
|
+
"vitest": "^4.1.5"
|
|
74
72
|
},
|
|
75
73
|
"dependencies": {
|
|
76
74
|
"byte-data": "^19.0.1",
|
package/src/bignum.ts
ADDED
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright © 2023-2026 Blockchain Commons, LLC
|
|
3
|
+
* Copyright © 2025-2026 Parity Technologies
|
|
4
|
+
*
|
|
5
|
+
*
|
|
6
|
+
* CBOR bignum (tags 2 and 3) support.
|
|
7
|
+
*
|
|
8
|
+
* This module provides conversion between CBOR and JavaScript BigInt types,
|
|
9
|
+
* implementing RFC 8949 §3.4.3 (Bignums) with dCBOR/CDE canonical encoding rules.
|
|
10
|
+
*
|
|
11
|
+
* Encoding:
|
|
12
|
+
* - `biguintToCbor` always encodes as tag 2 (positive bignum) with a byte
|
|
13
|
+
* string content.
|
|
14
|
+
* - `bigintToCbor` encodes as tag 2 for non-negative values or tag 3
|
|
15
|
+
* (negative bignum) for negative values.
|
|
16
|
+
* - No numeric reduction is performed: values are always encoded as bignums,
|
|
17
|
+
* even if they would fit in normal CBOR integers.
|
|
18
|
+
*
|
|
19
|
+
* Decoding:
|
|
20
|
+
* - Accepts CBOR integers (major types 0 and 1) and converts them to bigints.
|
|
21
|
+
* - Accepts tag 2 (positive bignum) and tag 3 (negative bignum) with byte
|
|
22
|
+
* string content.
|
|
23
|
+
* - Enforces shortest-form canonical representation for bignum magnitudes.
|
|
24
|
+
* - Rejects floating-point values.
|
|
25
|
+
*
|
|
26
|
+
* @module bignum
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
import { type Cbor, MajorType, toTaggedValue } from "./cbor";
|
|
30
|
+
import { CborError } from "./error";
|
|
31
|
+
|
|
32
|
+
// CBOR tag values (local constants matching tags.ts, avoiding circular import)
|
|
33
|
+
const TAG_2_POSITIVE_BIGNUM = 2;
|
|
34
|
+
const TAG_3_NEGATIVE_BIGNUM = 3;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Validates that a bignum magnitude byte string is in shortest canonical form.
|
|
38
|
+
*
|
|
39
|
+
* Matches Rust's `validate_bignum_magnitude()`.
|
|
40
|
+
*
|
|
41
|
+
* Rules:
|
|
42
|
+
* - For positive bignums (tag 2): empty byte string represents zero;
|
|
43
|
+
* non-empty must not have leading zero bytes.
|
|
44
|
+
* - For negative bignums (tag 3): byte string must not be empty
|
|
45
|
+
* (magnitude zero is encoded as `0x00`); must not have leading zero bytes
|
|
46
|
+
* except when the magnitude is zero (single `0x00`).
|
|
47
|
+
*
|
|
48
|
+
* @param bytes - The magnitude byte string to validate
|
|
49
|
+
* @param isNegative - Whether this is for a negative bignum (tag 3)
|
|
50
|
+
* @throws CborError with type NonCanonicalNumeric on validation failure
|
|
51
|
+
*/
|
|
52
|
+
export function validateBignumMagnitude(bytes: Uint8Array, isNegative: boolean): void {
|
|
53
|
+
if (isNegative) {
|
|
54
|
+
// Tag 3: byte string must not be empty
|
|
55
|
+
if (bytes.length === 0) {
|
|
56
|
+
throw new CborError({ type: "NonCanonicalNumeric" });
|
|
57
|
+
}
|
|
58
|
+
// No leading zeros unless the entire magnitude is zero (single 0x00 byte)
|
|
59
|
+
if (bytes.length > 1 && bytes[0] === 0) {
|
|
60
|
+
throw new CborError({ type: "NonCanonicalNumeric" });
|
|
61
|
+
}
|
|
62
|
+
} else {
|
|
63
|
+
// Tag 2: empty byte string is valid (represents zero)
|
|
64
|
+
// Non-empty must not have leading zeros
|
|
65
|
+
if (bytes.length > 0 && bytes[0] === 0) {
|
|
66
|
+
throw new CborError({ type: "NonCanonicalNumeric" });
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Strips leading zero bytes from a byte array, returning the minimal
|
|
73
|
+
* representation.
|
|
74
|
+
*
|
|
75
|
+
* Matches Rust's `strip_leading_zeros()`.
|
|
76
|
+
*
|
|
77
|
+
* @param bytes - The byte array to strip
|
|
78
|
+
* @returns A subarray with leading zeros removed
|
|
79
|
+
*/
|
|
80
|
+
export function stripLeadingZeros(bytes: Uint8Array): Uint8Array {
|
|
81
|
+
let start = 0;
|
|
82
|
+
while (start < bytes.length && bytes[start] === 0) {
|
|
83
|
+
start++;
|
|
84
|
+
}
|
|
85
|
+
return bytes.subarray(start);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Convert a non-negative bigint to a big-endian byte array.
|
|
90
|
+
*
|
|
91
|
+
* Zero returns an empty Uint8Array.
|
|
92
|
+
*
|
|
93
|
+
* @param value - A non-negative bigint value
|
|
94
|
+
* @returns Big-endian byte representation
|
|
95
|
+
*/
|
|
96
|
+
export function bigintToBytes(value: bigint): Uint8Array {
|
|
97
|
+
if (value === 0n) return new Uint8Array(0);
|
|
98
|
+
const hex = value.toString(16);
|
|
99
|
+
const padded = hex.length % 2 !== 0 ? `0${hex}` : hex;
|
|
100
|
+
const bytes = new Uint8Array(padded.length / 2);
|
|
101
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
102
|
+
bytes[i] = parseInt(padded.substring(i * 2, i * 2 + 2), 16);
|
|
103
|
+
}
|
|
104
|
+
return bytes;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Convert a big-endian byte array to a bigint.
|
|
109
|
+
*
|
|
110
|
+
* Empty array returns 0n.
|
|
111
|
+
*
|
|
112
|
+
* @param bytes - Big-endian byte representation
|
|
113
|
+
* @returns The bigint value
|
|
114
|
+
*/
|
|
115
|
+
export function bytesToBigint(bytes: Uint8Array): bigint {
|
|
116
|
+
if (bytes.length === 0) return 0n;
|
|
117
|
+
let result = 0n;
|
|
118
|
+
for (const byte of bytes) {
|
|
119
|
+
result = (result << 8n) | BigInt(byte);
|
|
120
|
+
}
|
|
121
|
+
return result;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Encode a non-negative bigint as a CBOR tag 2 (positive bignum).
|
|
126
|
+
*
|
|
127
|
+
* Matches Rust's `From<BigUint> for CBOR`.
|
|
128
|
+
*
|
|
129
|
+
* The value is always encoded as a bignum regardless of size.
|
|
130
|
+
* Zero is encoded as tag 2 with an empty byte string.
|
|
131
|
+
*
|
|
132
|
+
* @param value - A non-negative bigint (must be >= 0n)
|
|
133
|
+
* @returns CBOR tagged value
|
|
134
|
+
* @throws CborError with type OutOfRange if value is negative
|
|
135
|
+
*/
|
|
136
|
+
export function biguintToCbor(value: bigint): Cbor {
|
|
137
|
+
if (value < 0n) {
|
|
138
|
+
throw new CborError({ type: "OutOfRange" });
|
|
139
|
+
}
|
|
140
|
+
const bytes = bigintToBytes(value);
|
|
141
|
+
const stripped = stripLeadingZeros(bytes);
|
|
142
|
+
return toTaggedValue(TAG_2_POSITIVE_BIGNUM, stripped);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Encode a bigint as a CBOR tag 2 or tag 3 bignum.
|
|
147
|
+
*
|
|
148
|
+
* Matches Rust's `From<BigInt> for CBOR`.
|
|
149
|
+
*
|
|
150
|
+
* - Non-negative values use tag 2 (positive bignum).
|
|
151
|
+
* - Negative values use tag 3 (negative bignum), where the encoded
|
|
152
|
+
* magnitude is `|value| - 1` per RFC 8949.
|
|
153
|
+
*
|
|
154
|
+
* @param value - Any bigint value
|
|
155
|
+
* @returns CBOR tagged value
|
|
156
|
+
*/
|
|
157
|
+
export function bigintToCbor(value: bigint): Cbor {
|
|
158
|
+
if (value >= 0n) {
|
|
159
|
+
return biguintToCbor(value);
|
|
160
|
+
}
|
|
161
|
+
// Negative: use tag 3 with magnitude = |value| - 1
|
|
162
|
+
// For value = -1, magnitude = 1, so n = 0 -> encode as 0x00
|
|
163
|
+
// For value = -2, magnitude = 2, so n = 1 -> encode as 0x01
|
|
164
|
+
const magnitude = -value;
|
|
165
|
+
const n = magnitude - 1n;
|
|
166
|
+
const bytes = bigintToBytes(n);
|
|
167
|
+
const stripped = stripLeadingZeros(bytes);
|
|
168
|
+
// For n = 0 (value = -1), bigintToBytes returns empty, but we need 0x00
|
|
169
|
+
const contentBytes = stripped.length === 0 ? new Uint8Array([0]) : stripped;
|
|
170
|
+
return toTaggedValue(TAG_3_NEGATIVE_BIGNUM, contentBytes);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Decode a BigUint from an untagged CBOR byte string.
|
|
175
|
+
*
|
|
176
|
+
* Matches Rust's `biguint_from_untagged_cbor()`.
|
|
177
|
+
*
|
|
178
|
+
* This function is intended for use in tag summarizers where the tag has
|
|
179
|
+
* already been stripped. It expects a CBOR byte string representing the
|
|
180
|
+
* big-endian magnitude of a positive bignum (tag 2 content).
|
|
181
|
+
*
|
|
182
|
+
* Enforces canonical encoding: no leading zero bytes (except empty for zero).
|
|
183
|
+
*
|
|
184
|
+
* @param cbor - A CBOR value that should be a byte string
|
|
185
|
+
* @returns Non-negative bigint
|
|
186
|
+
* @throws CborError with type WrongType if not a byte string
|
|
187
|
+
* @throws CborError with type NonCanonicalNumeric if encoding is non-canonical
|
|
188
|
+
*/
|
|
189
|
+
export function biguintFromUntaggedCbor(cbor: Cbor): bigint {
|
|
190
|
+
if (cbor.type !== MajorType.ByteString) {
|
|
191
|
+
throw new CborError({ type: "WrongType" });
|
|
192
|
+
}
|
|
193
|
+
const bytes = cbor.value;
|
|
194
|
+
validateBignumMagnitude(bytes, false);
|
|
195
|
+
return bytesToBigint(bytes);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Decode a BigInt from an untagged CBOR byte string for a negative bignum.
|
|
200
|
+
*
|
|
201
|
+
* Matches Rust's `bigint_from_negative_untagged_cbor()`.
|
|
202
|
+
*
|
|
203
|
+
* This function is intended for use in tag summarizers where the tag has
|
|
204
|
+
* already been stripped. It expects a CBOR byte string representing `n` where
|
|
205
|
+
* the actual value is `-1 - n` (tag 3 content per RFC 8949).
|
|
206
|
+
*
|
|
207
|
+
* Enforces canonical encoding: no leading zero bytes (except single `0x00`
|
|
208
|
+
* for -1).
|
|
209
|
+
*
|
|
210
|
+
* @param cbor - A CBOR value that should be a byte string
|
|
211
|
+
* @returns Negative bigint
|
|
212
|
+
* @throws CborError with type WrongType if not a byte string
|
|
213
|
+
* @throws CborError with type NonCanonicalNumeric if encoding is non-canonical
|
|
214
|
+
*/
|
|
215
|
+
export function bigintFromNegativeUntaggedCbor(cbor: Cbor): bigint {
|
|
216
|
+
if (cbor.type !== MajorType.ByteString) {
|
|
217
|
+
throw new CborError({ type: "WrongType" });
|
|
218
|
+
}
|
|
219
|
+
const bytes = cbor.value;
|
|
220
|
+
validateBignumMagnitude(bytes, true);
|
|
221
|
+
const n = bytesToBigint(bytes);
|
|
222
|
+
const magnitude = n + 1n;
|
|
223
|
+
return -magnitude;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Convert CBOR to a non-negative bigint.
|
|
228
|
+
*
|
|
229
|
+
* Matches Rust's `TryFrom<CBOR> for BigUint`.
|
|
230
|
+
*
|
|
231
|
+
* Accepts:
|
|
232
|
+
* - Major type 0 (unsigned integer)
|
|
233
|
+
* - Tag 2 (positive bignum) with canonical byte string
|
|
234
|
+
*
|
|
235
|
+
* Rejects:
|
|
236
|
+
* - Major type 1 (negative integer) -> OutOfRange
|
|
237
|
+
* - Tag 3 (negative bignum) -> OutOfRange
|
|
238
|
+
* - Floating-point values -> WrongType
|
|
239
|
+
* - Non-canonical bignum encodings -> NonCanonicalNumeric
|
|
240
|
+
*
|
|
241
|
+
* @param cbor - The CBOR value to convert
|
|
242
|
+
* @returns Non-negative bigint
|
|
243
|
+
* @throws CborError
|
|
244
|
+
*/
|
|
245
|
+
export function cborToBiguint(cbor: Cbor): bigint {
|
|
246
|
+
switch (cbor.type) {
|
|
247
|
+
case MajorType.Unsigned:
|
|
248
|
+
return BigInt(cbor.value);
|
|
249
|
+
case MajorType.Negative:
|
|
250
|
+
throw new CborError({ type: "OutOfRange" });
|
|
251
|
+
case MajorType.Tagged: {
|
|
252
|
+
// bigint-aware comparison: avoids `Number(tag)` precision loss for
|
|
253
|
+
// tag values > MAX_SAFE_INTEGER.
|
|
254
|
+
if (tagEquals(cbor.tag, TAG_2_POSITIVE_BIGNUM)) {
|
|
255
|
+
const inner = cbor.value;
|
|
256
|
+
if (inner.type !== MajorType.ByteString) {
|
|
257
|
+
throw new CborError({ type: "WrongType" });
|
|
258
|
+
}
|
|
259
|
+
const bytes = inner.value;
|
|
260
|
+
validateBignumMagnitude(bytes, false);
|
|
261
|
+
return bytesToBigint(bytes);
|
|
262
|
+
} else if (tagEquals(cbor.tag, TAG_3_NEGATIVE_BIGNUM)) {
|
|
263
|
+
throw new CborError({ type: "OutOfRange" });
|
|
264
|
+
}
|
|
265
|
+
throw new CborError({ type: "WrongType" });
|
|
266
|
+
}
|
|
267
|
+
case MajorType.ByteString:
|
|
268
|
+
case MajorType.Text:
|
|
269
|
+
case MajorType.Array:
|
|
270
|
+
case MajorType.Map:
|
|
271
|
+
case MajorType.Simple:
|
|
272
|
+
throw new CborError({ type: "WrongType" });
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Compare a CBOR tag value (which may be `number` or `bigint`) against a
|
|
278
|
+
* small numeric literal. Avoids the `Number(tag) === literal` precision
|
|
279
|
+
* loss for huge bigint tags.
|
|
280
|
+
*/
|
|
281
|
+
function tagEquals(tag: number | bigint, literal: number): boolean {
|
|
282
|
+
return typeof tag === "bigint" ? tag === BigInt(literal) : tag === literal;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Convert CBOR to a bigint (any sign).
|
|
287
|
+
*
|
|
288
|
+
* Matches Rust's `TryFrom<CBOR> for BigInt`.
|
|
289
|
+
*
|
|
290
|
+
* Accepts:
|
|
291
|
+
* - Major type 0 (unsigned integer)
|
|
292
|
+
* - Major type 1 (negative integer)
|
|
293
|
+
* - Tag 2 (positive bignum) with canonical byte string
|
|
294
|
+
* - Tag 3 (negative bignum) with canonical byte string
|
|
295
|
+
*
|
|
296
|
+
* Rejects:
|
|
297
|
+
* - Floating-point values -> WrongType
|
|
298
|
+
* - Non-canonical bignum encodings -> NonCanonicalNumeric
|
|
299
|
+
*
|
|
300
|
+
* @param cbor - The CBOR value to convert
|
|
301
|
+
* @returns A bigint value
|
|
302
|
+
* @throws CborError
|
|
303
|
+
*/
|
|
304
|
+
export function cborToBigint(cbor: Cbor): bigint {
|
|
305
|
+
switch (cbor.type) {
|
|
306
|
+
case MajorType.Unsigned:
|
|
307
|
+
return BigInt(cbor.value);
|
|
308
|
+
case MajorType.Negative: {
|
|
309
|
+
// CBOR negative: value stored is n where actual = -1 - n
|
|
310
|
+
const n = BigInt(cbor.value);
|
|
311
|
+
const magnitude = n + 1n;
|
|
312
|
+
return -magnitude;
|
|
313
|
+
}
|
|
314
|
+
case MajorType.Tagged: {
|
|
315
|
+
if (tagEquals(cbor.tag, TAG_2_POSITIVE_BIGNUM)) {
|
|
316
|
+
const inner = cbor.value;
|
|
317
|
+
if (inner.type !== MajorType.ByteString) {
|
|
318
|
+
throw new CborError({ type: "WrongType" });
|
|
319
|
+
}
|
|
320
|
+
const bytes = inner.value;
|
|
321
|
+
validateBignumMagnitude(bytes, false);
|
|
322
|
+
const mag = bytesToBigint(bytes);
|
|
323
|
+
if (mag === 0n) {
|
|
324
|
+
return 0n;
|
|
325
|
+
}
|
|
326
|
+
return mag;
|
|
327
|
+
} else if (tagEquals(cbor.tag, TAG_3_NEGATIVE_BIGNUM)) {
|
|
328
|
+
const inner = cbor.value;
|
|
329
|
+
if (inner.type !== MajorType.ByteString) {
|
|
330
|
+
throw new CborError({ type: "WrongType" });
|
|
331
|
+
}
|
|
332
|
+
const bytes = inner.value;
|
|
333
|
+
validateBignumMagnitude(bytes, true);
|
|
334
|
+
const n = bytesToBigint(bytes);
|
|
335
|
+
const magnitude = n + 1n;
|
|
336
|
+
return -magnitude;
|
|
337
|
+
}
|
|
338
|
+
throw new CborError({ type: "WrongType" });
|
|
339
|
+
}
|
|
340
|
+
case MajorType.ByteString:
|
|
341
|
+
case MajorType.Text:
|
|
342
|
+
case MajorType.Array:
|
|
343
|
+
case MajorType.Map:
|
|
344
|
+
case MajorType.Simple:
|
|
345
|
+
throw new CborError({ type: "WrongType" });
|
|
346
|
+
}
|
|
347
|
+
}
|
package/src/byte-string.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
+
* Copyright © 2023-2026 Blockchain Commons, LLC
|
|
3
|
+
* Copyright © 2025-2026 Parity Technologies
|
|
4
|
+
*
|
|
5
|
+
*
|
|
2
6
|
* Byte string utilities for dCBOR.
|
|
3
7
|
*
|
|
4
8
|
* Represents a CBOR byte string (major type 2).
|
|
@@ -44,7 +48,7 @@ import { CborError } from "./error";
|
|
|
44
48
|
* ```
|
|
45
49
|
*/
|
|
46
50
|
export class ByteString {
|
|
47
|
-
|
|
51
|
+
private _data: Uint8Array;
|
|
48
52
|
|
|
49
53
|
/**
|
|
50
54
|
* Creates a new `ByteString` from a Uint8Array or array of bytes.
|
|
@@ -62,9 +66,9 @@ export class ByteString {
|
|
|
62
66
|
*/
|
|
63
67
|
constructor(data: Uint8Array | number[]) {
|
|
64
68
|
if (Array.isArray(data)) {
|
|
65
|
-
this
|
|
69
|
+
this._data = new Uint8Array(data);
|
|
66
70
|
} else {
|
|
67
|
-
this
|
|
71
|
+
this._data = new Uint8Array(data);
|
|
68
72
|
}
|
|
69
73
|
}
|
|
70
74
|
|
|
@@ -103,7 +107,7 @@ export class ByteString {
|
|
|
103
107
|
* ```
|
|
104
108
|
*/
|
|
105
109
|
data(): Uint8Array {
|
|
106
|
-
return this
|
|
110
|
+
return this._data;
|
|
107
111
|
}
|
|
108
112
|
|
|
109
113
|
/**
|
|
@@ -121,7 +125,7 @@ export class ByteString {
|
|
|
121
125
|
* ```
|
|
122
126
|
*/
|
|
123
127
|
len(): number {
|
|
124
|
-
return this
|
|
128
|
+
return this._data.length;
|
|
125
129
|
}
|
|
126
130
|
|
|
127
131
|
/**
|
|
@@ -139,7 +143,7 @@ export class ByteString {
|
|
|
139
143
|
* ```
|
|
140
144
|
*/
|
|
141
145
|
isEmpty(): boolean {
|
|
142
|
-
return this
|
|
146
|
+
return this._data.length === 0;
|
|
143
147
|
}
|
|
144
148
|
|
|
145
149
|
/**
|
|
@@ -160,10 +164,10 @@ export class ByteString {
|
|
|
160
164
|
*/
|
|
161
165
|
extend(other: Uint8Array | number[]): void {
|
|
162
166
|
const otherArray = Array.isArray(other) ? new Uint8Array(other) : other;
|
|
163
|
-
const newData = new Uint8Array(this
|
|
164
|
-
newData.set(this
|
|
165
|
-
newData.set(otherArray, this
|
|
166
|
-
this
|
|
167
|
+
const newData = new Uint8Array(this._data.length + otherArray.length);
|
|
168
|
+
newData.set(this._data, 0);
|
|
169
|
+
newData.set(otherArray, this._data.length);
|
|
170
|
+
this._data = newData;
|
|
167
171
|
}
|
|
168
172
|
|
|
169
173
|
/**
|
|
@@ -184,7 +188,7 @@ export class ByteString {
|
|
|
184
188
|
* ```
|
|
185
189
|
*/
|
|
186
190
|
toUint8Array(): Uint8Array {
|
|
187
|
-
return new Uint8Array(this
|
|
191
|
+
return new Uint8Array(this._data);
|
|
188
192
|
}
|
|
189
193
|
|
|
190
194
|
/**
|
|
@@ -211,7 +215,7 @@ export class ByteString {
|
|
|
211
215
|
* ```
|
|
212
216
|
*/
|
|
213
217
|
iter(): Iterator<number> {
|
|
214
|
-
return this
|
|
218
|
+
return this._data.values();
|
|
215
219
|
}
|
|
216
220
|
|
|
217
221
|
/**
|
|
@@ -233,7 +237,7 @@ export class ByteString {
|
|
|
233
237
|
* ```
|
|
234
238
|
*/
|
|
235
239
|
toCbor(): Cbor {
|
|
236
|
-
return toCbor(this
|
|
240
|
+
return toCbor(this._data);
|
|
237
241
|
}
|
|
238
242
|
|
|
239
243
|
/**
|
|
@@ -272,7 +276,7 @@ export class ByteString {
|
|
|
272
276
|
* @returns Byte at index or undefined
|
|
273
277
|
*/
|
|
274
278
|
at(index: number): number | undefined {
|
|
275
|
-
return this
|
|
279
|
+
return this._data[index];
|
|
276
280
|
}
|
|
277
281
|
|
|
278
282
|
/**
|
|
@@ -282,9 +286,9 @@ export class ByteString {
|
|
|
282
286
|
* @returns true if equal
|
|
283
287
|
*/
|
|
284
288
|
equals(other: ByteString): boolean {
|
|
285
|
-
if (this
|
|
286
|
-
for (let i = 0; i < this
|
|
287
|
-
if (this
|
|
289
|
+
if (this._data.length !== other._data.length) return false;
|
|
290
|
+
for (let i = 0; i < this._data.length; i++) {
|
|
291
|
+
if (this._data[i] !== other._data[i]) return false;
|
|
288
292
|
}
|
|
289
293
|
return true;
|
|
290
294
|
}
|
package/src/cbor-codable.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
+
* Copyright © 2023-2026 Blockchain Commons, LLC
|
|
3
|
+
* Copyright © 2025-2026 Parity Technologies
|
|
4
|
+
*
|
|
5
|
+
*
|
|
2
6
|
* Tagged CBOR decoding support.
|
|
3
7
|
*
|
|
4
8
|
* This module provides the `CborTaggedDecodable` interface, which enables types to
|
|
@@ -15,6 +19,7 @@ import { type Cbor, MajorType } from "./cbor";
|
|
|
15
19
|
import type { CborTagged } from "./cbor-tagged";
|
|
16
20
|
import type { Tag } from "./tag";
|
|
17
21
|
import { CborError } from "./error";
|
|
22
|
+
import { decodeCbor } from "./decode";
|
|
18
23
|
|
|
19
24
|
/**
|
|
20
25
|
* Interface for types that can be decoded from CBOR with a specific tag.
|
|
@@ -154,15 +159,16 @@ export const validateTag = (cbor: Cbor, expectedTags: Tag[]): Tag => {
|
|
|
154
159
|
throw new CborError({ type: "WrongType" });
|
|
155
160
|
}
|
|
156
161
|
|
|
157
|
-
const expectedValues = expectedTags.map((t) => t.value);
|
|
158
162
|
const tagValue = cbor.tag;
|
|
159
|
-
|
|
160
163
|
const matchingTag = expectedTags.find((t) => t.value === tagValue);
|
|
161
164
|
if (matchingTag === undefined) {
|
|
162
|
-
|
|
165
|
+
// Mirror Rust's `Error::WrongTag(expected, actual)` — produce the
|
|
166
|
+
// structured WrongTag variant rather than a stringly-typed Custom
|
|
167
|
+
// error so callers can branch on `error.type === "WrongTag"`.
|
|
163
168
|
throw new CborError({
|
|
164
|
-
type: "
|
|
165
|
-
|
|
169
|
+
type: "WrongTag",
|
|
170
|
+
expected: expectedTags[0],
|
|
171
|
+
actual: { value: tagValue },
|
|
166
172
|
});
|
|
167
173
|
}
|
|
168
174
|
|
|
@@ -182,3 +188,25 @@ export const extractTaggedContent = (cbor: Cbor): Cbor => {
|
|
|
182
188
|
}
|
|
183
189
|
return cbor.value;
|
|
184
190
|
};
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Default `fromTaggedCborData()` implementation — `decodeCbor(data)` then
|
|
194
|
+
* delegate to the decodable's `fromTaggedCbor()`.
|
|
195
|
+
*
|
|
196
|
+
* Mirrors Rust's default `from_tagged_cbor_data` impl on
|
|
197
|
+
* `CBORTaggedDecodable`.
|
|
198
|
+
*/
|
|
199
|
+
export function fromTaggedCborData<T>(decodable: CborTaggedDecodable<T>, data: Uint8Array): T {
|
|
200
|
+
return decodable.fromTaggedCbor(decodeCbor(data));
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Default `fromUntaggedCborData()` implementation — `decodeCbor(data)`
|
|
205
|
+
* then delegate to the decodable's `fromUntaggedCbor()`.
|
|
206
|
+
*
|
|
207
|
+
* Mirrors Rust's default `from_untagged_cbor_data` impl on
|
|
208
|
+
* `CBORTaggedDecodable`.
|
|
209
|
+
*/
|
|
210
|
+
export function fromUntaggedCborData<T>(decodable: CborTaggedDecodable<T>, data: Uint8Array): T {
|
|
211
|
+
return decodable.fromUntaggedCbor(decodeCbor(data));
|
|
212
|
+
}
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
+
* Copyright © 2023-2026 Blockchain Commons, LLC
|
|
3
|
+
* Copyright © 2025-2026 Parity Technologies
|
|
4
|
+
*
|
|
5
|
+
*
|
|
2
6
|
* Tagged CBOR encoding support.
|
|
3
7
|
*
|
|
4
8
|
* This module provides the `CborTaggedEncodable` interface, which enables types to
|
|
@@ -136,3 +140,14 @@ export const createTaggedCbor = (encodable: CborTaggedEncodable): Cbor => {
|
|
|
136
140
|
value: untagged,
|
|
137
141
|
});
|
|
138
142
|
};
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Default `taggedCborData()` implementation — `taggedCbor().toData()`.
|
|
146
|
+
*
|
|
147
|
+
* Mirrors Rust's default `tagged_cbor_data()` impl on
|
|
148
|
+
* `CBORTaggedEncodable`. TypeScript interfaces don't carry method bodies,
|
|
149
|
+
* so this helper plays the role of the Rust trait default; concrete types
|
|
150
|
+
* call it from their own `taggedCborData()` if they don't need to override.
|
|
151
|
+
*/
|
|
152
|
+
export const taggedCborData = (encodable: CborTaggedEncodable): Uint8Array =>
|
|
153
|
+
(encodable.taggedCbor() as { toData(): Uint8Array }).toData();
|
package/src/cbor-tagged.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
+
* Copyright © 2023-2026 Blockchain Commons, LLC
|
|
3
|
+
* Copyright © 2025-2026 Parity Technologies
|
|
4
|
+
*
|
|
5
|
+
*
|
|
2
6
|
* Base trait for types that have associated CBOR tags.
|
|
3
7
|
*
|
|
4
8
|
* CBOR allows values to be "tagged" with semantic information using tag
|
|
@@ -95,10 +99,16 @@ export interface CborTagged {
|
|
|
95
99
|
}
|
|
96
100
|
|
|
97
101
|
// Re-export interfaces and functions from separate modules for convenience
|
|
98
|
-
export {
|
|
102
|
+
export {
|
|
103
|
+
type CborTaggedEncodable,
|
|
104
|
+
createTaggedCbor,
|
|
105
|
+
taggedCborData,
|
|
106
|
+
} from "./cbor-tagged-encodable";
|
|
99
107
|
export {
|
|
100
108
|
type CborTaggedDecodable,
|
|
101
109
|
validateTag,
|
|
102
110
|
extractTaggedContent,
|
|
111
|
+
fromTaggedCborData,
|
|
112
|
+
fromUntaggedCborData,
|
|
103
113
|
} from "./cbor-tagged-decodable";
|
|
104
114
|
export { type CborTaggedCodable } from "./cbor-tagged-codable";
|