@bcts/provenance-mark 1.0.0-alpha.9 → 1.0.0-beta.1
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 +2 -1
- package/README.md +1 -1
- package/dist/index.cjs +1174 -584
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +489 -136
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +489 -136
- package/dist/index.d.mts.map +1 -1
- package/dist/index.iife.js +1247 -659
- package/dist/index.iife.js.map +1 -1
- package/dist/index.mjs +1132 -563
- package/dist/index.mjs.map +1 -1
- package/package.json +22 -22
- package/src/crypto-utils.ts +9 -3
- package/src/date.ts +42 -0
- package/src/envelope.ts +122 -0
- package/src/error.ts +21 -0
- package/src/generator.ts +153 -2
- package/src/index.ts +24 -0
- package/src/mark-info.ts +38 -17
- package/src/mark.ts +399 -45
- package/src/resolution.ts +32 -9
- package/src/rng-state.ts +6 -0
- package/src/seed.ts +6 -0
- package/src/utils.ts +68 -39
- package/src/validate.ts +63 -57
- package/src/xoshiro256starstar.ts +6 -0
package/dist/index.iife.js
CHANGED
|
@@ -1,58 +1,75 @@
|
|
|
1
|
-
var
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
var bctsProvenanceMark = (function(exports, _bcts_dcbor, _bcts_rand, _noble_hashes_sha2_js, _noble_hashes_hkdf_js, _noble_ciphers_chacha_js, _bcts_tags, _bcts_uniform_resources, _bcts_envelope) {
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
//#region src/error.ts
|
|
4
|
+
/**
|
|
5
|
+
* Copyright © 2023-2026 Blockchain Commons, LLC
|
|
6
|
+
* Copyright © 2025-2026 Parity Technologies
|
|
7
|
+
*
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
6
10
|
* Error types for Provenance Mark operations.
|
|
7
11
|
*/
|
|
8
|
-
let ProvenanceMarkErrorType = /* @__PURE__ */ function(ProvenanceMarkErrorType
|
|
12
|
+
let ProvenanceMarkErrorType = /* @__PURE__ */ function(ProvenanceMarkErrorType) {
|
|
9
13
|
/** Invalid Seed length */
|
|
10
|
-
ProvenanceMarkErrorType
|
|
14
|
+
ProvenanceMarkErrorType["InvalidSeedLength"] = "InvalidSeedLength";
|
|
11
15
|
/** Duplicate key */
|
|
12
|
-
ProvenanceMarkErrorType
|
|
16
|
+
ProvenanceMarkErrorType["DuplicateKey"] = "DuplicateKey";
|
|
13
17
|
/** Missing key */
|
|
14
|
-
ProvenanceMarkErrorType
|
|
18
|
+
ProvenanceMarkErrorType["MissingKey"] = "MissingKey";
|
|
15
19
|
/** Invalid key */
|
|
16
|
-
ProvenanceMarkErrorType
|
|
20
|
+
ProvenanceMarkErrorType["InvalidKey"] = "InvalidKey";
|
|
17
21
|
/** Extra keys */
|
|
18
|
-
ProvenanceMarkErrorType
|
|
22
|
+
ProvenanceMarkErrorType["ExtraKeys"] = "ExtraKeys";
|
|
19
23
|
/** Invalid key length for the given resolution */
|
|
20
|
-
ProvenanceMarkErrorType
|
|
24
|
+
ProvenanceMarkErrorType["InvalidKeyLength"] = "InvalidKeyLength";
|
|
21
25
|
/** Invalid next key length for the given resolution */
|
|
22
|
-
ProvenanceMarkErrorType
|
|
26
|
+
ProvenanceMarkErrorType["InvalidNextKeyLength"] = "InvalidNextKeyLength";
|
|
23
27
|
/** Invalid chain ID length for the given resolution */
|
|
24
|
-
ProvenanceMarkErrorType
|
|
28
|
+
ProvenanceMarkErrorType["InvalidChainIdLength"] = "InvalidChainIdLength";
|
|
25
29
|
/** Invalid message length for the given resolution */
|
|
26
|
-
ProvenanceMarkErrorType
|
|
30
|
+
ProvenanceMarkErrorType["InvalidMessageLength"] = "InvalidMessageLength";
|
|
27
31
|
/** Invalid CBOR data in info field */
|
|
28
|
-
ProvenanceMarkErrorType
|
|
32
|
+
ProvenanceMarkErrorType["InvalidInfoCbor"] = "InvalidInfoCbor";
|
|
29
33
|
/** Date out of range for serialization */
|
|
30
|
-
ProvenanceMarkErrorType
|
|
34
|
+
ProvenanceMarkErrorType["DateOutOfRange"] = "DateOutOfRange";
|
|
31
35
|
/** Invalid date components */
|
|
32
|
-
ProvenanceMarkErrorType
|
|
36
|
+
ProvenanceMarkErrorType["InvalidDate"] = "InvalidDate";
|
|
33
37
|
/** Missing required URL parameter */
|
|
34
|
-
ProvenanceMarkErrorType
|
|
38
|
+
ProvenanceMarkErrorType["MissingUrlParameter"] = "MissingUrlParameter";
|
|
35
39
|
/** Year out of range for 2-byte serialization */
|
|
36
|
-
ProvenanceMarkErrorType
|
|
40
|
+
ProvenanceMarkErrorType["YearOutOfRange"] = "YearOutOfRange";
|
|
37
41
|
/** Invalid month or day */
|
|
38
|
-
ProvenanceMarkErrorType
|
|
42
|
+
ProvenanceMarkErrorType["InvalidMonthOrDay"] = "InvalidMonthOrDay";
|
|
39
43
|
/** Resolution serialization error */
|
|
40
|
-
ProvenanceMarkErrorType
|
|
44
|
+
ProvenanceMarkErrorType["ResolutionError"] = "ResolutionError";
|
|
41
45
|
/** Bytewords encoding/decoding error */
|
|
42
|
-
ProvenanceMarkErrorType
|
|
46
|
+
ProvenanceMarkErrorType["BytewordsError"] = "BytewordsError";
|
|
43
47
|
/** CBOR encoding/decoding error */
|
|
44
|
-
ProvenanceMarkErrorType
|
|
48
|
+
ProvenanceMarkErrorType["CborError"] = "CborError";
|
|
45
49
|
/** URL parsing error */
|
|
46
|
-
ProvenanceMarkErrorType
|
|
50
|
+
ProvenanceMarkErrorType["UrlError"] = "UrlError";
|
|
47
51
|
/** Base64 decoding error */
|
|
48
|
-
ProvenanceMarkErrorType
|
|
52
|
+
ProvenanceMarkErrorType["Base64Error"] = "Base64Error";
|
|
49
53
|
/** JSON serialization error */
|
|
50
|
-
ProvenanceMarkErrorType
|
|
54
|
+
ProvenanceMarkErrorType["JsonError"] = "JsonError";
|
|
51
55
|
/** Integer conversion error */
|
|
52
|
-
ProvenanceMarkErrorType
|
|
56
|
+
ProvenanceMarkErrorType["IntegerConversionError"] = "IntegerConversionError";
|
|
53
57
|
/** Validation error */
|
|
54
|
-
ProvenanceMarkErrorType
|
|
55
|
-
|
|
58
|
+
ProvenanceMarkErrorType["ValidationError"] = "ValidationError";
|
|
59
|
+
/**
|
|
60
|
+
* Envelope serialization/deserialization error.
|
|
61
|
+
*
|
|
62
|
+
* Mirrors Rust `Error::Envelope(...)`
|
|
63
|
+
* (`provenance-mark-rust/src/error.rs`). The Rust enum surfaces
|
|
64
|
+
* envelope-format failures as their own variant; in earlier
|
|
65
|
+
* revisions of this port they collapsed into `CborError`. Both
|
|
66
|
+
* shapes are still emitted in practice (CBOR errors during envelope
|
|
67
|
+
* round-trip stay as `CborError`); this variant exists for the
|
|
68
|
+
* structural-level mismatches the Rust port tags as
|
|
69
|
+
* `Error::Envelope`.
|
|
70
|
+
*/
|
|
71
|
+
ProvenanceMarkErrorType["EnvelopeError"] = "EnvelopeError";
|
|
72
|
+
return ProvenanceMarkErrorType;
|
|
56
73
|
}({});
|
|
57
74
|
/**
|
|
58
75
|
* Error class for Provenance Mark operations.
|
|
@@ -75,37 +92,42 @@ var BCProvenanceMark = (function(exports, __bcts_dcbor, __noble_hashes_sha256, _
|
|
|
75
92
|
return JSON.stringify(value);
|
|
76
93
|
};
|
|
77
94
|
switch (type) {
|
|
78
|
-
case
|
|
79
|
-
case
|
|
80
|
-
case
|
|
81
|
-
case
|
|
82
|
-
case
|
|
83
|
-
case
|
|
84
|
-
case
|
|
85
|
-
case
|
|
86
|
-
case
|
|
87
|
-
case
|
|
88
|
-
case
|
|
89
|
-
case
|
|
90
|
-
case
|
|
91
|
-
case
|
|
92
|
-
case
|
|
93
|
-
case
|
|
94
|
-
case
|
|
95
|
-
case
|
|
96
|
-
case
|
|
97
|
-
case
|
|
98
|
-
case
|
|
99
|
-
case
|
|
100
|
-
case
|
|
95
|
+
case "InvalidSeedLength": return `invalid seed length: expected 32 bytes, got ${d("actual")} bytes`;
|
|
96
|
+
case "DuplicateKey": return `duplicate key: ${d("key")}`;
|
|
97
|
+
case "MissingKey": return `missing key: ${d("key")}`;
|
|
98
|
+
case "InvalidKey": return `invalid key: ${d("key")}`;
|
|
99
|
+
case "ExtraKeys": return `wrong number of keys: expected ${d("expected")}, got ${d("actual")}`;
|
|
100
|
+
case "InvalidKeyLength": return `invalid key length: expected ${d("expected")}, got ${d("actual")}`;
|
|
101
|
+
case "InvalidNextKeyLength": return `invalid next key length: expected ${d("expected")}, got ${d("actual")}`;
|
|
102
|
+
case "InvalidChainIdLength": return `invalid chain ID length: expected ${d("expected")}, got ${d("actual")}`;
|
|
103
|
+
case "InvalidMessageLength": return `invalid message length: expected at least ${d("expected")}, got ${d("actual")}`;
|
|
104
|
+
case "InvalidInfoCbor": return "invalid CBOR data in info field";
|
|
105
|
+
case "DateOutOfRange": return `date out of range: ${d("details")}`;
|
|
106
|
+
case "InvalidDate": return `invalid date: ${d("details")}`;
|
|
107
|
+
case "MissingUrlParameter": return `missing required URL parameter: ${d("parameter")}`;
|
|
108
|
+
case "YearOutOfRange": return `year out of range for 2-byte serialization: must be between 2023-2150, got ${d("year")}`;
|
|
109
|
+
case "InvalidMonthOrDay": return `invalid month (${d("month")}) or day (${d("day")}) for year ${d("year")}`;
|
|
110
|
+
case "ResolutionError": return `resolution serialization error: ${d("details")}`;
|
|
111
|
+
case "BytewordsError": return `bytewords error: ${d("message")}`;
|
|
112
|
+
case "CborError": return `CBOR error: ${d("message")}`;
|
|
113
|
+
case "UrlError": return `URL parsing error: ${d("message")}`;
|
|
114
|
+
case "Base64Error": return `base64 decoding error: ${d("message")}`;
|
|
115
|
+
case "JsonError": return `JSON error: ${d("message")}`;
|
|
116
|
+
case "IntegerConversionError": return `integer conversion error: ${d("message")}`;
|
|
117
|
+
case "ValidationError": return `validation error: ${d("message")}`;
|
|
118
|
+
case "EnvelopeError": return `envelope error: ${d("message")}`;
|
|
101
119
|
default: return type;
|
|
102
120
|
}
|
|
103
121
|
}
|
|
104
122
|
};
|
|
105
|
-
|
|
106
|
-
//#
|
|
107
|
-
|
|
108
|
-
|
|
123
|
+
//#endregion
|
|
124
|
+
//#region src/date.ts
|
|
125
|
+
/**
|
|
126
|
+
* Copyright © 2023-2026 Blockchain Commons, LLC
|
|
127
|
+
* Copyright © 2025-2026 Parity Technologies
|
|
128
|
+
*
|
|
129
|
+
*/
|
|
130
|
+
/**
|
|
109
131
|
* Reference date for 4-byte and 6-byte serialization (2001-01-01T00:00:00Z).
|
|
110
132
|
*/
|
|
111
133
|
const REFERENCE_DATE = Date.UTC(2001, 0, 1, 0, 0, 0, 0);
|
|
@@ -136,8 +158,8 @@ var BCProvenanceMark = (function(exports, __bcts_dcbor, __noble_hashes_sha256, _
|
|
|
136
158
|
const month = date.getUTCMonth() + 1;
|
|
137
159
|
const day = date.getUTCDate();
|
|
138
160
|
const yy = year - 2023;
|
|
139
|
-
if (yy < 0 || yy >= 128) throw new ProvenanceMarkError(
|
|
140
|
-
if (month < 1 || month > 12 || day < 1 || day > 31) throw new ProvenanceMarkError(
|
|
161
|
+
if (yy < 0 || yy >= 128) throw new ProvenanceMarkError("YearOutOfRange", void 0, { year });
|
|
162
|
+
if (month < 1 || month > 12 || day < 1 || day > 31) throw new ProvenanceMarkError("InvalidMonthOrDay", void 0, {
|
|
141
163
|
year,
|
|
142
164
|
month,
|
|
143
165
|
day
|
|
@@ -152,12 +174,12 @@ var BCProvenanceMark = (function(exports, __bcts_dcbor, __noble_hashes_sha256, _
|
|
|
152
174
|
* Deserialize 2 bytes to a date.
|
|
153
175
|
*/
|
|
154
176
|
function deserialize2Bytes(bytes) {
|
|
155
|
-
if (bytes.length !== 2) throw new ProvenanceMarkError(
|
|
177
|
+
if (bytes.length !== 2) throw new ProvenanceMarkError("InvalidDate", void 0, { details: `expected 2 bytes, got ${bytes.length}` });
|
|
156
178
|
const value = bytes[0] << 8 | bytes[1];
|
|
157
179
|
const day = value & 31;
|
|
158
180
|
const month = value >> 5 & 15;
|
|
159
181
|
const year = (value >> 9 & 127) + 2023;
|
|
160
|
-
if (month < 1 || month > 12 || !isValidDay(year, month, day)) throw new ProvenanceMarkError(
|
|
182
|
+
if (month < 1 || month > 12 || !isValidDay(year, month, day)) throw new ProvenanceMarkError("InvalidMonthOrDay", void 0, {
|
|
161
183
|
year,
|
|
162
184
|
month,
|
|
163
185
|
day
|
|
@@ -170,7 +192,7 @@ var BCProvenanceMark = (function(exports, __bcts_dcbor, __noble_hashes_sha256, _
|
|
|
170
192
|
function serialize4Bytes(date) {
|
|
171
193
|
const duration = date.getTime() - REFERENCE_DATE;
|
|
172
194
|
const seconds = Math.floor(duration / 1e3);
|
|
173
|
-
if (seconds < 0 || seconds > 4294967295) throw new ProvenanceMarkError(
|
|
195
|
+
if (seconds < 0 || seconds > 4294967295) throw new ProvenanceMarkError("DateOutOfRange", void 0, { details: "seconds value out of range for u32" });
|
|
174
196
|
const buf = new Uint8Array(4);
|
|
175
197
|
buf[0] = seconds >> 24 & 255;
|
|
176
198
|
buf[1] = seconds >> 16 & 255;
|
|
@@ -182,7 +204,7 @@ var BCProvenanceMark = (function(exports, __bcts_dcbor, __noble_hashes_sha256, _
|
|
|
182
204
|
* Deserialize 4 bytes to a date.
|
|
183
205
|
*/
|
|
184
206
|
function deserialize4Bytes(bytes) {
|
|
185
|
-
if (bytes.length !== 4) throw new ProvenanceMarkError(
|
|
207
|
+
if (bytes.length !== 4) throw new ProvenanceMarkError("InvalidDate", void 0, { details: `expected 4 bytes, got ${bytes.length}` });
|
|
186
208
|
const seconds = (bytes[0] << 24 | bytes[1] << 16 | bytes[2] << 8 | bytes[3]) >>> 0;
|
|
187
209
|
return new Date(REFERENCE_DATE + seconds * 1e3);
|
|
188
210
|
}
|
|
@@ -192,7 +214,7 @@ var BCProvenanceMark = (function(exports, __bcts_dcbor, __noble_hashes_sha256, _
|
|
|
192
214
|
function serialize6Bytes(date) {
|
|
193
215
|
const duration = date.getTime() - REFERENCE_DATE;
|
|
194
216
|
const milliseconds = BigInt(duration);
|
|
195
|
-
if (milliseconds < 0n || milliseconds > BigInt(MAX_6_BYTE_VALUE)) throw new ProvenanceMarkError(
|
|
217
|
+
if (milliseconds < 0n || milliseconds > BigInt(MAX_6_BYTE_VALUE)) throw new ProvenanceMarkError("DateOutOfRange", void 0, { details: "date exceeds maximum representable value" });
|
|
196
218
|
const buf = new Uint8Array(6);
|
|
197
219
|
buf[0] = Number(milliseconds >> 40n & 255n);
|
|
198
220
|
buf[1] = Number(milliseconds >> 32n & 255n);
|
|
@@ -206,9 +228,9 @@ var BCProvenanceMark = (function(exports, __bcts_dcbor, __noble_hashes_sha256, _
|
|
|
206
228
|
* Deserialize 6 bytes to a date.
|
|
207
229
|
*/
|
|
208
230
|
function deserialize6Bytes(bytes) {
|
|
209
|
-
if (bytes.length !== 6) throw new ProvenanceMarkError(
|
|
231
|
+
if (bytes.length !== 6) throw new ProvenanceMarkError("InvalidDate", void 0, { details: `expected 6 bytes, got ${bytes.length}` });
|
|
210
232
|
const milliseconds = BigInt(bytes[0]) << 40n | BigInt(bytes[1]) << 32n | BigInt(bytes[2]) << 24n | BigInt(bytes[3]) << 16n | BigInt(bytes[4]) << 8n | BigInt(bytes[5]);
|
|
211
|
-
if (milliseconds > BigInt(MAX_6_BYTE_VALUE)) throw new ProvenanceMarkError(
|
|
233
|
+
if (milliseconds > BigInt(MAX_6_BYTE_VALUE)) throw new ProvenanceMarkError("DateOutOfRange", void 0, { details: "date exceeds maximum representable value" });
|
|
212
234
|
return new Date(REFERENCE_DATE + Number(milliseconds));
|
|
213
235
|
}
|
|
214
236
|
/**
|
|
@@ -231,7 +253,7 @@ var BCProvenanceMark = (function(exports, __bcts_dcbor, __noble_hashes_sha256, _
|
|
|
231
253
|
*/
|
|
232
254
|
function dateFromIso8601(str) {
|
|
233
255
|
const date = new Date(str);
|
|
234
|
-
if (isNaN(date.getTime())) throw new ProvenanceMarkError(
|
|
256
|
+
if (isNaN(date.getTime())) throw new ProvenanceMarkError("InvalidDate", void 0, { details: `cannot parse date: ${str}` });
|
|
235
257
|
return date;
|
|
236
258
|
}
|
|
237
259
|
/**
|
|
@@ -240,19 +262,50 @@ var BCProvenanceMark = (function(exports, __bcts_dcbor, __noble_hashes_sha256, _
|
|
|
240
262
|
function dateToDateString(date) {
|
|
241
263
|
return `${date.getUTCFullYear()}-${(date.getUTCMonth() + 1).toString().padStart(2, "0")}-${date.getUTCDate().toString().padStart(2, "0")}`;
|
|
242
264
|
}
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
265
|
+
/**
|
|
266
|
+
* Renders a `Date` the way Rust's `dcbor::Date::Display` does
|
|
267
|
+
* (`bc-dcbor-rust/src/date.rs:485-492`):
|
|
268
|
+
*
|
|
269
|
+
* - When the UTC time is exactly `00:00:00` (subsecond precision is
|
|
270
|
+
* ignored — Rust's check is `hour == 0 && minute == 0 && second == 0`,
|
|
271
|
+
* matching `chrono::SecondsFormat::Secs`), emit just `YYYY-MM-DD`.
|
|
272
|
+
* - Otherwise emit RFC 3339 with second precision (no fractional
|
|
273
|
+
* seconds), e.g. `2023-02-08T15:30:45Z`.
|
|
274
|
+
*
|
|
275
|
+
* This is the canonical "Rust string" for dates across the
|
|
276
|
+
* provenance-mark public surface — `mark.toDebugString`,
|
|
277
|
+
* `mark.precedesOpt` `DateOrdering` issue, `markdownSummary`, and the
|
|
278
|
+
* validation report's `DateOrdering` payload all use it. Centralising
|
|
279
|
+
* here keeps every call site in lockstep with the Rust output.
|
|
280
|
+
*
|
|
281
|
+
* @example
|
|
282
|
+
* ```typescript
|
|
283
|
+
* dateToDisplay(new Date("2023-06-20T00:00:00Z")); // "2023-06-20"
|
|
284
|
+
* dateToDisplay(new Date("2023-06-20T15:30:45Z")); // "2023-06-20T15:30:45Z"
|
|
285
|
+
* dateToDisplay(new Date("2023-06-20T15:30:45.123Z")); // "2023-06-20T15:30:45Z"
|
|
286
|
+
* ```
|
|
287
|
+
*/
|
|
288
|
+
function dateToDisplay(date) {
|
|
289
|
+
if (!(date.getUTCHours() !== 0 || date.getUTCMinutes() !== 0 || date.getUTCSeconds() !== 0)) return dateToDateString(date);
|
|
290
|
+
return date.toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
291
|
+
}
|
|
292
|
+
//#endregion
|
|
293
|
+
//#region src/resolution.ts
|
|
294
|
+
/**
|
|
295
|
+
* Copyright © 2023-2026 Blockchain Commons, LLC
|
|
296
|
+
* Copyright © 2025-2026 Parity Technologies
|
|
297
|
+
*
|
|
298
|
+
*/
|
|
299
|
+
/**
|
|
247
300
|
* Resolution levels for provenance marks.
|
|
248
301
|
* Higher resolution provides more security but larger mark sizes.
|
|
249
302
|
*/
|
|
250
|
-
let ProvenanceMarkResolution = /* @__PURE__ */ function(ProvenanceMarkResolution
|
|
251
|
-
ProvenanceMarkResolution
|
|
252
|
-
ProvenanceMarkResolution
|
|
253
|
-
ProvenanceMarkResolution
|
|
254
|
-
ProvenanceMarkResolution
|
|
255
|
-
return ProvenanceMarkResolution
|
|
303
|
+
let ProvenanceMarkResolution = /* @__PURE__ */ function(ProvenanceMarkResolution) {
|
|
304
|
+
ProvenanceMarkResolution[ProvenanceMarkResolution["Low"] = 0] = "Low";
|
|
305
|
+
ProvenanceMarkResolution[ProvenanceMarkResolution["Medium"] = 1] = "Medium";
|
|
306
|
+
ProvenanceMarkResolution[ProvenanceMarkResolution["Quartile"] = 2] = "Quartile";
|
|
307
|
+
ProvenanceMarkResolution[ProvenanceMarkResolution["High"] = 3] = "High";
|
|
308
|
+
return ProvenanceMarkResolution;
|
|
256
309
|
}({});
|
|
257
310
|
/**
|
|
258
311
|
* Convert a resolution to its numeric value.
|
|
@@ -265,11 +318,11 @@ var BCProvenanceMark = (function(exports, __bcts_dcbor, __noble_hashes_sha256, _
|
|
|
265
318
|
*/
|
|
266
319
|
function resolutionFromNumber(value) {
|
|
267
320
|
switch (value) {
|
|
268
|
-
case 0: return
|
|
269
|
-
case 1: return
|
|
270
|
-
case 2: return
|
|
271
|
-
case 3: return
|
|
272
|
-
default: throw new ProvenanceMarkError(
|
|
321
|
+
case 0: return 0;
|
|
322
|
+
case 1: return 1;
|
|
323
|
+
case 2: return 2;
|
|
324
|
+
case 3: return 3;
|
|
325
|
+
default: throw new ProvenanceMarkError("ResolutionError", void 0, { details: `invalid provenance mark resolution value: ${value}` });
|
|
273
326
|
}
|
|
274
327
|
}
|
|
275
328
|
/**
|
|
@@ -277,10 +330,10 @@ var BCProvenanceMark = (function(exports, __bcts_dcbor, __noble_hashes_sha256, _
|
|
|
277
330
|
*/
|
|
278
331
|
function linkLength(res) {
|
|
279
332
|
switch (res) {
|
|
280
|
-
case
|
|
281
|
-
case
|
|
282
|
-
case
|
|
283
|
-
case
|
|
333
|
+
case 0: return 4;
|
|
334
|
+
case 1: return 8;
|
|
335
|
+
case 2: return 16;
|
|
336
|
+
case 3: return 32;
|
|
284
337
|
}
|
|
285
338
|
}
|
|
286
339
|
/**
|
|
@@ -288,10 +341,10 @@ var BCProvenanceMark = (function(exports, __bcts_dcbor, __noble_hashes_sha256, _
|
|
|
288
341
|
*/
|
|
289
342
|
function seqBytesLength(res) {
|
|
290
343
|
switch (res) {
|
|
291
|
-
case
|
|
292
|
-
case
|
|
293
|
-
case
|
|
294
|
-
case
|
|
344
|
+
case 0: return 2;
|
|
345
|
+
case 1:
|
|
346
|
+
case 2:
|
|
347
|
+
case 3: return 4;
|
|
295
348
|
}
|
|
296
349
|
}
|
|
297
350
|
/**
|
|
@@ -299,10 +352,10 @@ var BCProvenanceMark = (function(exports, __bcts_dcbor, __noble_hashes_sha256, _
|
|
|
299
352
|
*/
|
|
300
353
|
function dateBytesLength(res) {
|
|
301
354
|
switch (res) {
|
|
302
|
-
case
|
|
303
|
-
case
|
|
304
|
-
case
|
|
305
|
-
case
|
|
355
|
+
case 0: return 2;
|
|
356
|
+
case 1: return 4;
|
|
357
|
+
case 2:
|
|
358
|
+
case 3: return 6;
|
|
306
359
|
}
|
|
307
360
|
}
|
|
308
361
|
/**
|
|
@@ -370,10 +423,10 @@ var BCProvenanceMark = (function(exports, __bcts_dcbor, __noble_hashes_sha256, _
|
|
|
370
423
|
*/
|
|
371
424
|
function serializeDate(res, date) {
|
|
372
425
|
switch (res) {
|
|
373
|
-
case
|
|
374
|
-
case
|
|
375
|
-
case
|
|
376
|
-
case
|
|
426
|
+
case 0: return serialize2Bytes(date);
|
|
427
|
+
case 1: return serialize4Bytes(date);
|
|
428
|
+
case 2:
|
|
429
|
+
case 3: return serialize6Bytes(date);
|
|
377
430
|
}
|
|
378
431
|
}
|
|
379
432
|
/**
|
|
@@ -381,46 +434,55 @@ var BCProvenanceMark = (function(exports, __bcts_dcbor, __noble_hashes_sha256, _
|
|
|
381
434
|
*/
|
|
382
435
|
function deserializeDate(res, data) {
|
|
383
436
|
switch (res) {
|
|
384
|
-
case
|
|
385
|
-
if (data.length !== 2) throw new ProvenanceMarkError(
|
|
437
|
+
case 0:
|
|
438
|
+
if (data.length !== 2) throw new ProvenanceMarkError("ResolutionError", void 0, { details: `invalid date length: expected 2 bytes, got ${data.length}` });
|
|
386
439
|
return deserialize2Bytes(data);
|
|
387
|
-
case
|
|
388
|
-
if (data.length !== 4) throw new ProvenanceMarkError(
|
|
440
|
+
case 1:
|
|
441
|
+
if (data.length !== 4) throw new ProvenanceMarkError("ResolutionError", void 0, { details: `invalid date length: expected 4 bytes, got ${data.length}` });
|
|
389
442
|
return deserialize4Bytes(data);
|
|
390
|
-
case
|
|
391
|
-
case
|
|
392
|
-
if (data.length !== 6) throw new ProvenanceMarkError(
|
|
443
|
+
case 2:
|
|
444
|
+
case 3:
|
|
445
|
+
if (data.length !== 6) throw new ProvenanceMarkError("ResolutionError", void 0, { details: `invalid date length: expected 6 bytes, got ${data.length}` });
|
|
393
446
|
return deserialize6Bytes(data);
|
|
394
447
|
}
|
|
395
448
|
}
|
|
396
449
|
/**
|
|
397
450
|
* Serialize a sequence number into bytes based on the resolution.
|
|
451
|
+
*
|
|
452
|
+
* Mirrors Rust's typed `u32` parameter (`generator.rs::serialize_seq`)
|
|
453
|
+
* — the input must be a non-negative integer in `[0, 2^32-1]` (a u32).
|
|
454
|
+
* For Low resolution the upper bound additionally narrows to `2^16-1`
|
|
455
|
+
* (a u16) per Rust `if seq > 0xFFFF`. Earlier revisions of this port
|
|
456
|
+
* accepted any JS `number` for the 4-byte branch and would silently
|
|
457
|
+
* truncate values above `2^32-1`; now we raise `ResolutionError` so
|
|
458
|
+
* the wire output never deviates from Rust's u32 contract.
|
|
398
459
|
*/
|
|
399
460
|
function serializeSeq(res, seq) {
|
|
461
|
+
if (!Number.isInteger(seq) || seq < 0) throw new ProvenanceMarkError("ResolutionError", void 0, { details: `sequence number must be a non-negative integer, got ${seq}` });
|
|
400
462
|
if (seqBytesLength(res) === 2) {
|
|
401
|
-
if (seq > 65535) throw new ProvenanceMarkError(
|
|
463
|
+
if (seq > 65535) throw new ProvenanceMarkError("ResolutionError", void 0, { details: `sequence number ${seq} out of range for 2-byte format (max 65535)` });
|
|
402
464
|
const buf = new Uint8Array(2);
|
|
403
465
|
buf[0] = seq >> 8 & 255;
|
|
404
466
|
buf[1] = seq & 255;
|
|
405
467
|
return buf;
|
|
406
|
-
} else {
|
|
407
|
-
const buf = new Uint8Array(4);
|
|
408
|
-
buf[0] = seq >> 24 & 255;
|
|
409
|
-
buf[1] = seq >> 16 & 255;
|
|
410
|
-
buf[2] = seq >> 8 & 255;
|
|
411
|
-
buf[3] = seq & 255;
|
|
412
|
-
return buf;
|
|
413
468
|
}
|
|
469
|
+
if (seq > 4294967295) throw new ProvenanceMarkError("ResolutionError", void 0, { details: `sequence number ${seq} out of range for 4-byte format (max 4294967295)` });
|
|
470
|
+
const buf = new Uint8Array(4);
|
|
471
|
+
buf[0] = seq >>> 24 & 255;
|
|
472
|
+
buf[1] = seq >>> 16 & 255;
|
|
473
|
+
buf[2] = seq >>> 8 & 255;
|
|
474
|
+
buf[3] = seq & 255;
|
|
475
|
+
return buf;
|
|
414
476
|
}
|
|
415
477
|
/**
|
|
416
478
|
* Deserialize bytes into a sequence number based on the resolution.
|
|
417
479
|
*/
|
|
418
480
|
function deserializeSeq(res, data) {
|
|
419
481
|
if (seqBytesLength(res) === 2) {
|
|
420
|
-
if (data.length !== 2) throw new ProvenanceMarkError(
|
|
482
|
+
if (data.length !== 2) throw new ProvenanceMarkError("ResolutionError", void 0, { details: `invalid sequence number length: expected 2 bytes, got ${data.length}` });
|
|
421
483
|
return data[0] << 8 | data[1];
|
|
422
484
|
} else {
|
|
423
|
-
if (data.length !== 4) throw new ProvenanceMarkError(
|
|
485
|
+
if (data.length !== 4) throw new ProvenanceMarkError("ResolutionError", void 0, { details: `invalid sequence number length: expected 4 bytes, got ${data.length}` });
|
|
424
486
|
return (data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3]) >>> 0;
|
|
425
487
|
}
|
|
426
488
|
}
|
|
@@ -429,34 +491,38 @@ var BCProvenanceMark = (function(exports, __bcts_dcbor, __noble_hashes_sha256, _
|
|
|
429
491
|
*/
|
|
430
492
|
function resolutionToString(res) {
|
|
431
493
|
switch (res) {
|
|
432
|
-
case
|
|
433
|
-
case
|
|
434
|
-
case
|
|
435
|
-
case
|
|
494
|
+
case 0: return "low";
|
|
495
|
+
case 1: return "medium";
|
|
496
|
+
case 2: return "quartile";
|
|
497
|
+
case 3: return "high";
|
|
436
498
|
}
|
|
437
499
|
}
|
|
438
500
|
/**
|
|
439
501
|
* Convert a resolution to CBOR.
|
|
440
502
|
*/
|
|
441
503
|
function resolutionToCbor(res) {
|
|
442
|
-
return (0,
|
|
504
|
+
return (0, _bcts_dcbor.cbor)(res);
|
|
443
505
|
}
|
|
444
506
|
/**
|
|
445
507
|
* Create a resolution from CBOR.
|
|
446
508
|
*/
|
|
447
509
|
function resolutionFromCbor(cborValue) {
|
|
448
|
-
const value = (0,
|
|
510
|
+
const value = (0, _bcts_dcbor.expectUnsigned)(cborValue);
|
|
449
511
|
return resolutionFromNumber(Number(value));
|
|
450
512
|
}
|
|
451
|
-
|
|
452
|
-
//#
|
|
453
|
-
|
|
513
|
+
//#endregion
|
|
514
|
+
//#region src/crypto-utils.ts
|
|
515
|
+
/**
|
|
516
|
+
* Copyright © 2023-2026 Blockchain Commons, LLC
|
|
517
|
+
* Copyright © 2025-2026 Parity Technologies
|
|
518
|
+
*
|
|
519
|
+
*/
|
|
454
520
|
const SHA256_SIZE = 32;
|
|
455
521
|
/**
|
|
456
522
|
* Compute SHA-256 hash of data.
|
|
457
523
|
*/
|
|
458
524
|
function sha256(data) {
|
|
459
|
-
return (0,
|
|
525
|
+
return (0, _noble_hashes_sha2_js.sha256)(data);
|
|
460
526
|
}
|
|
461
527
|
/**
|
|
462
528
|
* Compute SHA-256 hash and return a prefix of the given length.
|
|
@@ -474,7 +540,7 @@ var BCProvenanceMark = (function(exports, __bcts_dcbor, __noble_hashes_sha256, _
|
|
|
474
540
|
* Compute HKDF-HMAC-SHA-256 for the given key material.
|
|
475
541
|
*/
|
|
476
542
|
function hkdfHmacSha256(keyMaterial, salt, keyLen) {
|
|
477
|
-
return (0,
|
|
543
|
+
return (0, _noble_hashes_hkdf_js.hkdf)(_noble_hashes_sha2_js.sha256, keyMaterial, salt.length > 0 ? salt : void 0, new Uint8Array(0), keyLen);
|
|
478
544
|
}
|
|
479
545
|
/**
|
|
480
546
|
* Obfuscate (or deobfuscate) a message using ChaCha20.
|
|
@@ -485,12 +551,183 @@ var BCProvenanceMark = (function(exports, __bcts_dcbor, __noble_hashes_sha256, _
|
|
|
485
551
|
const extendedKey = extendKey(key instanceof Uint8Array ? key : new Uint8Array(key));
|
|
486
552
|
const iv = new Uint8Array(12);
|
|
487
553
|
for (let i = 0; i < 12; i++) iv[i] = extendedKey[31 - i];
|
|
488
|
-
return (0,
|
|
554
|
+
return (0, _noble_ciphers_chacha_js.chacha20)(extendedKey, iv, message instanceof Uint8Array ? message : new Uint8Array(message));
|
|
489
555
|
}
|
|
490
|
-
|
|
491
|
-
//#
|
|
492
|
-
|
|
493
|
-
|
|
556
|
+
//#endregion
|
|
557
|
+
//#region src/seed.ts
|
|
558
|
+
/**
|
|
559
|
+
* Copyright © 2023-2026 Blockchain Commons, LLC
|
|
560
|
+
* Copyright © 2025-2026 Parity Technologies
|
|
561
|
+
*
|
|
562
|
+
*/
|
|
563
|
+
const PROVENANCE_SEED_LENGTH = 32;
|
|
564
|
+
/**
|
|
565
|
+
* A seed for generating provenance marks.
|
|
566
|
+
*/
|
|
567
|
+
var ProvenanceSeed = class ProvenanceSeed {
|
|
568
|
+
data;
|
|
569
|
+
constructor(data) {
|
|
570
|
+
this.data = data;
|
|
571
|
+
}
|
|
572
|
+
/**
|
|
573
|
+
* Create a new random seed using secure random number generation.
|
|
574
|
+
*/
|
|
575
|
+
static new() {
|
|
576
|
+
const data = (0, _bcts_rand.randomData)(32);
|
|
577
|
+
return ProvenanceSeed.fromBytes(data);
|
|
578
|
+
}
|
|
579
|
+
/**
|
|
580
|
+
* Create a new seed using custom random data.
|
|
581
|
+
*/
|
|
582
|
+
static newUsing(randomData) {
|
|
583
|
+
if (randomData.length < 32) throw new ProvenanceMarkError("InvalidSeedLength", void 0, { actual: randomData.length });
|
|
584
|
+
return ProvenanceSeed.fromBytes(randomData.slice(0, 32));
|
|
585
|
+
}
|
|
586
|
+
/**
|
|
587
|
+
* Create a new seed from a passphrase.
|
|
588
|
+
*/
|
|
589
|
+
static newWithPassphrase(passphrase) {
|
|
590
|
+
const seedData = extendKey(new TextEncoder().encode(passphrase));
|
|
591
|
+
return ProvenanceSeed.fromBytes(seedData);
|
|
592
|
+
}
|
|
593
|
+
/**
|
|
594
|
+
* Get the raw bytes.
|
|
595
|
+
*/
|
|
596
|
+
toBytes() {
|
|
597
|
+
return new Uint8Array(this.data);
|
|
598
|
+
}
|
|
599
|
+
/**
|
|
600
|
+
* Create from a 32-byte array.
|
|
601
|
+
*/
|
|
602
|
+
static fromBytes(bytes) {
|
|
603
|
+
if (bytes.length !== 32) throw new ProvenanceMarkError("InvalidSeedLength", void 0, { actual: bytes.length });
|
|
604
|
+
return new ProvenanceSeed(new Uint8Array(bytes));
|
|
605
|
+
}
|
|
606
|
+
/**
|
|
607
|
+
* Create from a slice (validates length).
|
|
608
|
+
*/
|
|
609
|
+
static fromSlice(bytes) {
|
|
610
|
+
return ProvenanceSeed.fromBytes(bytes);
|
|
611
|
+
}
|
|
612
|
+
/**
|
|
613
|
+
* Get the hex representation.
|
|
614
|
+
*/
|
|
615
|
+
hex() {
|
|
616
|
+
return Array.from(this.data).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
617
|
+
}
|
|
618
|
+
/**
|
|
619
|
+
* Convert to CBOR (byte string).
|
|
620
|
+
*/
|
|
621
|
+
toCbor() {
|
|
622
|
+
return (0, _bcts_dcbor.cbor)(this.data);
|
|
623
|
+
}
|
|
624
|
+
/**
|
|
625
|
+
* Create from CBOR (byte string).
|
|
626
|
+
*/
|
|
627
|
+
static fromCbor(cborValue) {
|
|
628
|
+
const bytes = (0, _bcts_dcbor.expectBytes)(cborValue);
|
|
629
|
+
return ProvenanceSeed.fromBytes(bytes);
|
|
630
|
+
}
|
|
631
|
+
};
|
|
632
|
+
//#endregion
|
|
633
|
+
//#region src/utils.ts
|
|
634
|
+
/**
|
|
635
|
+
* Copyright © 2023-2026 Blockchain Commons, LLC
|
|
636
|
+
* Copyright © 2025-2026 Parity Technologies
|
|
637
|
+
*
|
|
638
|
+
*
|
|
639
|
+
* Utility functions for byte array conversions.
|
|
640
|
+
*
|
|
641
|
+
* These functions provide cross-platform support for common byte manipulation
|
|
642
|
+
* operations needed in provenance mark encoding.
|
|
643
|
+
*/
|
|
644
|
+
/**
|
|
645
|
+
* Convert a Uint8Array to a lowercase hexadecimal string.
|
|
646
|
+
*
|
|
647
|
+
* @param data - The byte array to convert
|
|
648
|
+
* @returns A lowercase hex string representation (2 characters per byte)
|
|
649
|
+
*/
|
|
650
|
+
function bytesToHex(data) {
|
|
651
|
+
return Array.from(data).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
652
|
+
}
|
|
653
|
+
/**
|
|
654
|
+
* Convert a Uint8Array to a base64-encoded string.
|
|
655
|
+
*
|
|
656
|
+
* This function works in both browser and Node.js environments.
|
|
657
|
+
*
|
|
658
|
+
* @param data - The byte array to encode
|
|
659
|
+
* @returns A base64-encoded string
|
|
660
|
+
*/
|
|
661
|
+
function toBase64(data) {
|
|
662
|
+
let binary = "";
|
|
663
|
+
for (const byte of data) binary += String.fromCharCode(byte);
|
|
664
|
+
return btoa(binary);
|
|
665
|
+
}
|
|
666
|
+
/**
|
|
667
|
+
* Convert a base64-encoded string to a Uint8Array.
|
|
668
|
+
*
|
|
669
|
+
* This function works in both browser and Node.js environments.
|
|
670
|
+
*
|
|
671
|
+
* @param base64 - A base64-encoded string
|
|
672
|
+
* @returns The decoded byte array
|
|
673
|
+
*/
|
|
674
|
+
function fromBase64(base64) {
|
|
675
|
+
const binary = atob(base64);
|
|
676
|
+
const bytes = new Uint8Array(binary.length);
|
|
677
|
+
for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
|
|
678
|
+
return bytes;
|
|
679
|
+
}
|
|
680
|
+
/**
|
|
681
|
+
* Parse a base64-encoded provenance seed.
|
|
682
|
+
*
|
|
683
|
+
* Mirrors Rust's `parse_seed` user helper
|
|
684
|
+
* (`provenance-mark-rust/src/util.rs:34-38`), which round-trips the
|
|
685
|
+
* input through serde JSON / `deserialize_block` and so requires the
|
|
686
|
+
* decoded bytes to be exactly {@link PROVENANCE_SEED_LENGTH} (32) long.
|
|
687
|
+
* The TS equivalent decodes the base64 directly and delegates to
|
|
688
|
+
* {@link ProvenanceSeed.fromBytes} for the length check.
|
|
689
|
+
*
|
|
690
|
+
* @param s - Base64-encoded 32-byte seed string.
|
|
691
|
+
* @returns The decoded {@link ProvenanceSeed}.
|
|
692
|
+
* @throws {ProvenanceMarkError} If the input is not valid base64 or the
|
|
693
|
+
* decoded length is not exactly 32 bytes.
|
|
694
|
+
*/
|
|
695
|
+
function parseSeed(s) {
|
|
696
|
+
let bytes;
|
|
697
|
+
try {
|
|
698
|
+
bytes = fromBase64(s);
|
|
699
|
+
} catch (e) {
|
|
700
|
+
throw new ProvenanceMarkError("Base64Error", "invalid base64 encoding for provenance seed", { details: e instanceof Error ? e.message : String(e) });
|
|
701
|
+
}
|
|
702
|
+
return ProvenanceSeed.fromBytes(bytes);
|
|
703
|
+
}
|
|
704
|
+
/**
|
|
705
|
+
* Parse a date string (`YYYY-MM-DD` or full RFC 3339) into a `Date`.
|
|
706
|
+
*
|
|
707
|
+
* Mirrors Rust's `parse_date` user helper
|
|
708
|
+
* (`provenance-mark-rust/src/util.rs:40-42`), which delegates to
|
|
709
|
+
* `Date::from_string` (the same parser the JSON deserializer uses).
|
|
710
|
+
* Accepts the same shapes the Rust parser does:
|
|
711
|
+
*
|
|
712
|
+
* - `YYYY-MM-DD` (interpreted as UTC midnight, matching JS spec).
|
|
713
|
+
* - Full RFC 3339, e.g. `2023-06-20T15:30:45Z` or
|
|
714
|
+
* `2023-06-20T15:30:45.123Z`.
|
|
715
|
+
*
|
|
716
|
+
* @throws {ProvenanceMarkError} If the input fails to parse.
|
|
717
|
+
*/
|
|
718
|
+
function parseDate(s) {
|
|
719
|
+
const date = new Date(s);
|
|
720
|
+
if (Number.isNaN(date.getTime())) throw new ProvenanceMarkError("InvalidDate", `cannot parse date: ${s}`);
|
|
721
|
+
return date;
|
|
722
|
+
}
|
|
723
|
+
//#endregion
|
|
724
|
+
//#region src/xoshiro256starstar.ts
|
|
725
|
+
/**
|
|
726
|
+
* Copyright © 2023-2026 Blockchain Commons, LLC
|
|
727
|
+
* Copyright © 2025-2026 Parity Technologies
|
|
728
|
+
*
|
|
729
|
+
*/
|
|
730
|
+
/**
|
|
494
731
|
* Xoshiro256** PRNG implementation.
|
|
495
732
|
* A fast, high-quality pseudorandom number generator.
|
|
496
733
|
*/
|
|
@@ -610,9 +847,13 @@ var BCProvenanceMark = (function(exports, __bcts_dcbor, __noble_hashes_sha256, _
|
|
|
610
847
|
this.s[3] = this.rotateLeft64(this.s[3], 45n);
|
|
611
848
|
}
|
|
612
849
|
};
|
|
613
|
-
|
|
614
|
-
//#
|
|
615
|
-
|
|
850
|
+
//#endregion
|
|
851
|
+
//#region src/rng-state.ts
|
|
852
|
+
/**
|
|
853
|
+
* Copyright © 2023-2026 Blockchain Commons, LLC
|
|
854
|
+
* Copyright © 2025-2026 Parity Technologies
|
|
855
|
+
*
|
|
856
|
+
*/
|
|
616
857
|
const RNG_STATE_LENGTH = 32;
|
|
617
858
|
/**
|
|
618
859
|
* RNG state for provenance marks (32 bytes).
|
|
@@ -632,7 +873,7 @@ var BCProvenanceMark = (function(exports, __bcts_dcbor, __noble_hashes_sha256, _
|
|
|
632
873
|
* Create from a 32-byte array.
|
|
633
874
|
*/
|
|
634
875
|
static fromBytes(bytes) {
|
|
635
|
-
if (bytes.length !==
|
|
876
|
+
if (bytes.length !== 32) throw new ProvenanceMarkError("InvalidSeedLength", void 0, { actual: bytes.length });
|
|
636
877
|
return new RngState(new Uint8Array(bytes));
|
|
637
878
|
}
|
|
638
879
|
/**
|
|
@@ -651,149 +892,302 @@ var BCProvenanceMark = (function(exports, __bcts_dcbor, __noble_hashes_sha256, _
|
|
|
651
892
|
* Convert to CBOR (byte string).
|
|
652
893
|
*/
|
|
653
894
|
toCbor() {
|
|
654
|
-
return (0,
|
|
895
|
+
return (0, _bcts_dcbor.cbor)(this.data);
|
|
655
896
|
}
|
|
656
897
|
/**
|
|
657
898
|
* Create from CBOR (byte string).
|
|
658
899
|
*/
|
|
659
900
|
static fromCbor(cborValue) {
|
|
660
|
-
const bytes = (0,
|
|
901
|
+
const bytes = (0, _bcts_dcbor.expectBytes)(cborValue);
|
|
661
902
|
return RngState.fromBytes(bytes);
|
|
662
903
|
}
|
|
663
904
|
};
|
|
664
|
-
|
|
665
|
-
//#
|
|
666
|
-
//#region src/seed.ts
|
|
667
|
-
const PROVENANCE_SEED_LENGTH = 32;
|
|
905
|
+
//#endregion
|
|
906
|
+
//#region src/validate.ts
|
|
668
907
|
/**
|
|
669
|
-
*
|
|
908
|
+
* Format for validation report output.
|
|
670
909
|
*/
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
/**
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
}
|
|
683
|
-
/**
|
|
684
|
-
* Create a new seed using custom random data.
|
|
685
|
-
*/
|
|
686
|
-
static newUsing(randomData$1) {
|
|
687
|
-
if (randomData$1.length < PROVENANCE_SEED_LENGTH) throw new ProvenanceMarkError(ProvenanceMarkErrorType.InvalidSeedLength, void 0, { actual: randomData$1.length });
|
|
688
|
-
return ProvenanceSeed.fromBytes(randomData$1.slice(0, PROVENANCE_SEED_LENGTH));
|
|
689
|
-
}
|
|
690
|
-
/**
|
|
691
|
-
* Create a new seed from a passphrase.
|
|
692
|
-
*/
|
|
693
|
-
static newWithPassphrase(passphrase) {
|
|
694
|
-
const seedData = extendKey(new TextEncoder().encode(passphrase));
|
|
695
|
-
return ProvenanceSeed.fromBytes(seedData);
|
|
696
|
-
}
|
|
697
|
-
/**
|
|
698
|
-
* Get the raw bytes.
|
|
699
|
-
*/
|
|
700
|
-
toBytes() {
|
|
701
|
-
return new Uint8Array(this.data);
|
|
702
|
-
}
|
|
703
|
-
/**
|
|
704
|
-
* Create from a 32-byte array.
|
|
705
|
-
*/
|
|
706
|
-
static fromBytes(bytes) {
|
|
707
|
-
if (bytes.length !== PROVENANCE_SEED_LENGTH) throw new ProvenanceMarkError(ProvenanceMarkErrorType.InvalidSeedLength, void 0, { actual: bytes.length });
|
|
708
|
-
return new ProvenanceSeed(new Uint8Array(bytes));
|
|
709
|
-
}
|
|
710
|
-
/**
|
|
711
|
-
* Create from a slice (validates length).
|
|
712
|
-
*/
|
|
713
|
-
static fromSlice(bytes) {
|
|
714
|
-
return ProvenanceSeed.fromBytes(bytes);
|
|
715
|
-
}
|
|
716
|
-
/**
|
|
717
|
-
* Get the hex representation.
|
|
718
|
-
*/
|
|
719
|
-
hex() {
|
|
720
|
-
return Array.from(this.data).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
721
|
-
}
|
|
722
|
-
/**
|
|
723
|
-
* Convert to CBOR (byte string).
|
|
724
|
-
*/
|
|
725
|
-
toCbor() {
|
|
726
|
-
return (0, __bcts_dcbor.cbor)(this.data);
|
|
727
|
-
}
|
|
728
|
-
/**
|
|
729
|
-
* Create from CBOR (byte string).
|
|
730
|
-
*/
|
|
731
|
-
static fromCbor(cborValue) {
|
|
732
|
-
const bytes = (0, __bcts_dcbor.expectBytes)(cborValue);
|
|
733
|
-
return ProvenanceSeed.fromBytes(bytes);
|
|
734
|
-
}
|
|
735
|
-
};
|
|
736
|
-
|
|
737
|
-
//#endregion
|
|
738
|
-
//#region src/utils.ts
|
|
739
|
-
/**
|
|
740
|
-
* Convert a Uint8Array to a lowercase hexadecimal string.
|
|
741
|
-
*
|
|
742
|
-
* @param data - The byte array to convert
|
|
743
|
-
* @returns A lowercase hex string representation (2 characters per byte)
|
|
910
|
+
let ValidationReportFormat = /* @__PURE__ */ function(ValidationReportFormat) {
|
|
911
|
+
/** Human-readable text format */
|
|
912
|
+
ValidationReportFormat["Text"] = "text";
|
|
913
|
+
/** Compact JSON format (no whitespace) */
|
|
914
|
+
ValidationReportFormat["JsonCompact"] = "json-compact";
|
|
915
|
+
/** Pretty-printed JSON format (with indentation) */
|
|
916
|
+
ValidationReportFormat["JsonPretty"] = "json-pretty";
|
|
917
|
+
return ValidationReportFormat;
|
|
918
|
+
}({});
|
|
919
|
+
/**
|
|
920
|
+
* Format a validation issue as a string.
|
|
744
921
|
*/
|
|
745
|
-
function
|
|
746
|
-
|
|
922
|
+
function formatValidationIssue(issue) {
|
|
923
|
+
switch (issue.type) {
|
|
924
|
+
case "HashMismatch": return `hash mismatch: expected ${issue.expected}, got ${issue.actual}`;
|
|
925
|
+
case "KeyMismatch": return "key mismatch: current hash was not generated from next key";
|
|
926
|
+
case "SequenceGap": return `sequence number gap: expected ${issue.expected}, got ${issue.actual}`;
|
|
927
|
+
case "DateOrdering": return `date must be equal or later: previous is ${issue.previous}, next is ${issue.next}`;
|
|
928
|
+
case "NonGenesisAtZero": return "non-genesis mark at sequence 0";
|
|
929
|
+
case "InvalidGenesisKey": return "genesis mark must have key equal to chain_id";
|
|
930
|
+
}
|
|
747
931
|
}
|
|
748
932
|
/**
|
|
749
|
-
*
|
|
750
|
-
*
|
|
751
|
-
* This function works in both browser and Node.js environments.
|
|
752
|
-
*
|
|
753
|
-
* @param data - The byte array to encode
|
|
754
|
-
* @returns A base64-encoded string
|
|
933
|
+
* Get the chain ID as a hex string for display.
|
|
755
934
|
*/
|
|
756
|
-
function
|
|
757
|
-
|
|
758
|
-
if (typeof globalBtoa === "function") {
|
|
759
|
-
let binary = "";
|
|
760
|
-
for (const byte of data) binary += String.fromCharCode(byte);
|
|
761
|
-
return globalBtoa(binary);
|
|
762
|
-
}
|
|
763
|
-
const requireFn = require;
|
|
764
|
-
if (typeof requireFn === "function") {
|
|
765
|
-
const { Buffer: NodeBuffer } = requireFn("buffer");
|
|
766
|
-
return NodeBuffer.from(data).toString("base64");
|
|
767
|
-
}
|
|
768
|
-
throw new Error("btoa not available and require is not defined");
|
|
935
|
+
function chainIdHex(report) {
|
|
936
|
+
return hexEncode(report.chainId);
|
|
769
937
|
}
|
|
770
938
|
/**
|
|
771
|
-
*
|
|
772
|
-
*
|
|
773
|
-
* This function works in both browser and Node.js environments.
|
|
774
|
-
*
|
|
775
|
-
* @param base64 - A base64-encoded string
|
|
776
|
-
* @returns The decoded byte array
|
|
939
|
+
* Check if the validation report has any issues.
|
|
777
940
|
*/
|
|
778
|
-
function
|
|
779
|
-
const
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
941
|
+
function hasIssues(report) {
|
|
942
|
+
for (const chain of report.chains) if (!chain.hasGenesis) return true;
|
|
943
|
+
for (const chain of report.chains) for (const seq of chain.sequences) for (const mark of seq.marks) if (mark.issues.length > 0) return true;
|
|
944
|
+
if (report.chains.length > 1) return true;
|
|
945
|
+
if (report.chains.length === 1 && report.chains[0].sequences.length > 1) return true;
|
|
946
|
+
return false;
|
|
947
|
+
}
|
|
948
|
+
/**
|
|
949
|
+
* Check if the validation report contains interesting information.
|
|
950
|
+
*/
|
|
951
|
+
function isInteresting(report) {
|
|
952
|
+
if (report.chains.length === 0) return false;
|
|
953
|
+
for (const chain of report.chains) if (!chain.hasGenesis) return true;
|
|
954
|
+
if (report.chains.length === 1) {
|
|
955
|
+
const chain = report.chains[0];
|
|
956
|
+
if (chain.sequences.length === 1) {
|
|
957
|
+
if (chain.sequences[0].marks.every((m) => m.issues.length === 0)) return false;
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
return true;
|
|
961
|
+
}
|
|
962
|
+
/**
|
|
963
|
+
* Format the validation report as human-readable text.
|
|
964
|
+
*/
|
|
965
|
+
function formatText(report) {
|
|
966
|
+
if (!isInteresting(report)) return "";
|
|
967
|
+
const lines = [];
|
|
968
|
+
lines.push(`Total marks: ${report.marks.length}`);
|
|
969
|
+
lines.push(`Chains: ${report.chains.length}`);
|
|
970
|
+
lines.push("");
|
|
971
|
+
for (let chainIdx = 0; chainIdx < report.chains.length; chainIdx++) {
|
|
972
|
+
const chain = report.chains[chainIdx];
|
|
973
|
+
const chainIdStr = chainIdHex(chain);
|
|
974
|
+
const shortChainId = chainIdStr.length > 8 ? chainIdStr.slice(0, 8) : chainIdStr;
|
|
975
|
+
lines.push(`Chain ${chainIdx + 1}: ${shortChainId}`);
|
|
976
|
+
if (!chain.hasGenesis) lines.push(" Warning: No genesis mark found");
|
|
977
|
+
for (const seq of chain.sequences) for (const flaggedMark of seq.marks) {
|
|
978
|
+
const mark = flaggedMark.mark;
|
|
979
|
+
const shortId = mark.idHex().slice(0, 8);
|
|
980
|
+
const seqNum = mark.seq();
|
|
981
|
+
const annotations = [];
|
|
982
|
+
if (mark.isGenesis()) annotations.push("genesis mark");
|
|
983
|
+
for (const issue of flaggedMark.issues) {
|
|
984
|
+
let issueStr;
|
|
985
|
+
switch (issue.type) {
|
|
986
|
+
case "SequenceGap":
|
|
987
|
+
issueStr = `gap: ${issue.expected} missing`;
|
|
988
|
+
break;
|
|
989
|
+
case "DateOrdering":
|
|
990
|
+
issueStr = `date ${issue.previous} < ${issue.next}`;
|
|
991
|
+
break;
|
|
992
|
+
case "HashMismatch":
|
|
993
|
+
issueStr = "hash mismatch";
|
|
994
|
+
break;
|
|
995
|
+
case "KeyMismatch":
|
|
996
|
+
issueStr = "key mismatch";
|
|
997
|
+
break;
|
|
998
|
+
case "NonGenesisAtZero":
|
|
999
|
+
issueStr = "non-genesis at seq 0";
|
|
1000
|
+
break;
|
|
1001
|
+
case "InvalidGenesisKey":
|
|
1002
|
+
issueStr = "invalid genesis key";
|
|
1003
|
+
break;
|
|
1004
|
+
}
|
|
1005
|
+
annotations.push(issueStr);
|
|
1006
|
+
}
|
|
1007
|
+
if (annotations.length === 0) lines.push(` ${seqNum}: ${shortId}`);
|
|
1008
|
+
else lines.push(` ${seqNum}: ${shortId} (${annotations.join(", ")})`);
|
|
1009
|
+
}
|
|
1010
|
+
lines.push("");
|
|
1011
|
+
}
|
|
1012
|
+
return lines.join("\n").trimEnd();
|
|
1013
|
+
}
|
|
1014
|
+
/**
|
|
1015
|
+
* Format the validation report.
|
|
1016
|
+
*/
|
|
1017
|
+
function formatReport(report, format) {
|
|
1018
|
+
switch (format) {
|
|
1019
|
+
case "text": return formatText(report);
|
|
1020
|
+
case "json-compact": return JSON.stringify(reportToJSON(report));
|
|
1021
|
+
case "json-pretty": return JSON.stringify(reportToJSON(report), null, 2);
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
/**
|
|
1025
|
+
* Convert a report to a JSON-serializable object.
|
|
1026
|
+
*/
|
|
1027
|
+
function reportToJSON(report) {
|
|
1028
|
+
return {
|
|
1029
|
+
marks: report.marks.map((m) => m.urString()),
|
|
1030
|
+
chains: report.chains.map((chain) => ({
|
|
1031
|
+
chain_id: hexEncode(chain.chainId),
|
|
1032
|
+
has_genesis: chain.hasGenesis,
|
|
1033
|
+
marks: chain.marks.map((m) => m.urString()),
|
|
1034
|
+
sequences: chain.sequences.map((seq) => ({
|
|
1035
|
+
start_seq: seq.startSeq,
|
|
1036
|
+
end_seq: seq.endSeq,
|
|
1037
|
+
marks: seq.marks.map((fm) => ({
|
|
1038
|
+
mark: fm.mark.urString(),
|
|
1039
|
+
issues: fm.issues.map(issueToJSON)
|
|
1040
|
+
}))
|
|
1041
|
+
}))
|
|
1042
|
+
}))
|
|
1043
|
+
};
|
|
1044
|
+
}
|
|
1045
|
+
/**
|
|
1046
|
+
* Convert a ValidationIssue to JSON matching Rust's serde format.
|
|
1047
|
+
*
|
|
1048
|
+
* Rust uses `#[serde(tag = "type", content = "data")]` which wraps
|
|
1049
|
+
* struct variant data in a `"data"` field. Unit variants have no
|
|
1050
|
+
* `"data"` field.
|
|
1051
|
+
*/
|
|
1052
|
+
function issueToJSON(issue) {
|
|
1053
|
+
switch (issue.type) {
|
|
1054
|
+
case "HashMismatch": return {
|
|
1055
|
+
type: "HashMismatch",
|
|
1056
|
+
data: {
|
|
1057
|
+
expected: issue.expected,
|
|
1058
|
+
actual: issue.actual
|
|
1059
|
+
}
|
|
1060
|
+
};
|
|
1061
|
+
case "SequenceGap": return {
|
|
1062
|
+
type: "SequenceGap",
|
|
1063
|
+
data: {
|
|
1064
|
+
expected: issue.expected,
|
|
1065
|
+
actual: issue.actual
|
|
1066
|
+
}
|
|
1067
|
+
};
|
|
1068
|
+
case "DateOrdering": return {
|
|
1069
|
+
type: "DateOrdering",
|
|
1070
|
+
data: {
|
|
1071
|
+
previous: issue.previous,
|
|
1072
|
+
next: issue.next
|
|
1073
|
+
}
|
|
1074
|
+
};
|
|
1075
|
+
case "KeyMismatch": return { type: "KeyMismatch" };
|
|
1076
|
+
case "NonGenesisAtZero": return { type: "NonGenesisAtZero" };
|
|
1077
|
+
case "InvalidGenesisKey": return { type: "InvalidGenesisKey" };
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
/**
|
|
1081
|
+
* Build sequence bins for a chain.
|
|
1082
|
+
*/
|
|
1083
|
+
function buildSequenceBins(marks) {
|
|
1084
|
+
const sequences = [];
|
|
1085
|
+
let currentSequence = [];
|
|
1086
|
+
for (let i = 0; i < marks.length; i++) {
|
|
1087
|
+
const mark = marks[i];
|
|
1088
|
+
if (i === 0) currentSequence.push({
|
|
1089
|
+
mark,
|
|
1090
|
+
issues: []
|
|
1091
|
+
});
|
|
1092
|
+
else {
|
|
1093
|
+
const prev = marks[i - 1];
|
|
1094
|
+
try {
|
|
1095
|
+
prev.precedesOpt(mark);
|
|
1096
|
+
currentSequence.push({
|
|
1097
|
+
mark,
|
|
1098
|
+
issues: []
|
|
1099
|
+
});
|
|
1100
|
+
} catch (e) {
|
|
1101
|
+
if (currentSequence.length > 0) sequences.push(createSequenceReport(currentSequence));
|
|
1102
|
+
let issue;
|
|
1103
|
+
if (e instanceof ProvenanceMarkError && e.details?.["validationIssue"] !== void 0) issue = e.details["validationIssue"];
|
|
1104
|
+
else issue = { type: "KeyMismatch" };
|
|
1105
|
+
currentSequence = [{
|
|
1106
|
+
mark,
|
|
1107
|
+
issues: [issue]
|
|
1108
|
+
}];
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
if (currentSequence.length > 0) sequences.push(createSequenceReport(currentSequence));
|
|
1113
|
+
return sequences;
|
|
1114
|
+
}
|
|
1115
|
+
/**
|
|
1116
|
+
* Create a sequence report from flagged marks.
|
|
1117
|
+
*/
|
|
1118
|
+
function createSequenceReport(marks) {
|
|
1119
|
+
return {
|
|
1120
|
+
startSeq: marks.length > 0 ? marks[0].mark.seq() : 0,
|
|
1121
|
+
endSeq: marks.length > 0 ? marks[marks.length - 1].mark.seq() : 0,
|
|
1122
|
+
marks
|
|
1123
|
+
};
|
|
1124
|
+
}
|
|
1125
|
+
/**
|
|
1126
|
+
* Validate a collection of provenance marks.
|
|
1127
|
+
*/
|
|
1128
|
+
function validate(marks) {
|
|
1129
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1130
|
+
const deduplicatedMarks = [];
|
|
1131
|
+
for (const mark of marks) {
|
|
1132
|
+
const key = `${mark.res()}:${hexEncode(mark.message())}`;
|
|
1133
|
+
if (!seen.has(key)) {
|
|
1134
|
+
seen.add(key);
|
|
1135
|
+
deduplicatedMarks.push(mark);
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
const chainBins = /* @__PURE__ */ new Map();
|
|
1139
|
+
for (const mark of deduplicatedMarks) {
|
|
1140
|
+
const chainIdKey = hexEncode(mark.chainId());
|
|
1141
|
+
const bin = chainBins.get(chainIdKey);
|
|
1142
|
+
if (bin !== void 0) bin.push(mark);
|
|
1143
|
+
else chainBins.set(chainIdKey, [mark]);
|
|
1144
|
+
}
|
|
1145
|
+
const chains = [];
|
|
1146
|
+
for (const [chainIdKey, chainMarks] of chainBins) {
|
|
1147
|
+
chainMarks.sort((a, b) => a.seq() - b.seq());
|
|
1148
|
+
const hasGenesis = chainMarks.length > 0 && chainMarks[0].seq() === 0 && chainMarks[0].isGenesis();
|
|
1149
|
+
const sequences = buildSequenceBins(chainMarks);
|
|
1150
|
+
chains.push({
|
|
1151
|
+
chainId: hexDecode(chainIdKey),
|
|
1152
|
+
hasGenesis,
|
|
1153
|
+
marks: chainMarks,
|
|
1154
|
+
sequences
|
|
1155
|
+
});
|
|
1156
|
+
}
|
|
1157
|
+
chains.sort((a, b) => {
|
|
1158
|
+
const aHex = hexEncode(a.chainId);
|
|
1159
|
+
const bHex = hexEncode(b.chainId);
|
|
1160
|
+
if (aHex < bHex) return -1;
|
|
1161
|
+
if (aHex > bHex) return 1;
|
|
1162
|
+
return 0;
|
|
1163
|
+
});
|
|
1164
|
+
return {
|
|
1165
|
+
marks: deduplicatedMarks,
|
|
1166
|
+
chains
|
|
1167
|
+
};
|
|
1168
|
+
}
|
|
1169
|
+
/**
|
|
1170
|
+
* Helper function to encode bytes as hex.
|
|
1171
|
+
*/
|
|
1172
|
+
function hexEncode(bytes) {
|
|
1173
|
+
return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
1174
|
+
}
|
|
1175
|
+
/**
|
|
1176
|
+
* Helper function to decode hex to bytes.
|
|
1177
|
+
*/
|
|
1178
|
+
function hexDecode(hex) {
|
|
1179
|
+
const bytes = new Uint8Array(hex.length / 2);
|
|
1180
|
+
for (let i = 0; i < hex.length; i += 2) bytes[i / 2] = parseInt(hex.slice(i, i + 2), 16);
|
|
1181
|
+
return bytes;
|
|
1182
|
+
}
|
|
1183
|
+
//#endregion
|
|
1184
|
+
//#region src/mark.ts
|
|
1185
|
+
/**
|
|
1186
|
+
* Copyright © 2023-2026 Blockchain Commons, LLC
|
|
1187
|
+
* Copyright © 2025-2026 Parity Technologies
|
|
1188
|
+
*
|
|
1189
|
+
*/
|
|
1190
|
+
/**
|
|
797
1191
|
* A cryptographically-secured provenance mark.
|
|
798
1192
|
*/
|
|
799
1193
|
var ProvenanceMark = class ProvenanceMark {
|
|
@@ -860,29 +1254,29 @@ var BCProvenanceMark = (function(exports, __bcts_dcbor, __noble_hashes_sha256, _
|
|
|
860
1254
|
*/
|
|
861
1255
|
info() {
|
|
862
1256
|
if (this._infoBytes.length === 0) return;
|
|
863
|
-
return (0,
|
|
1257
|
+
return (0, _bcts_dcbor.decodeCbor)(this._infoBytes);
|
|
864
1258
|
}
|
|
865
1259
|
/**
|
|
866
1260
|
* Create a new provenance mark.
|
|
867
1261
|
*/
|
|
868
1262
|
static new(res, key, nextKey, chainId, seq, date, info) {
|
|
869
1263
|
const linkLen = linkLength(res);
|
|
870
|
-
if (key.length !== linkLen) throw new ProvenanceMarkError(
|
|
1264
|
+
if (key.length !== linkLen) throw new ProvenanceMarkError("InvalidKeyLength", void 0, {
|
|
871
1265
|
expected: linkLen,
|
|
872
1266
|
actual: key.length
|
|
873
1267
|
});
|
|
874
|
-
if (nextKey.length !== linkLen) throw new ProvenanceMarkError(
|
|
1268
|
+
if (nextKey.length !== linkLen) throw new ProvenanceMarkError("InvalidNextKeyLength", void 0, {
|
|
875
1269
|
expected: linkLen,
|
|
876
1270
|
actual: nextKey.length
|
|
877
1271
|
});
|
|
878
|
-
if (chainId.length !== linkLen) throw new ProvenanceMarkError(
|
|
1272
|
+
if (chainId.length !== linkLen) throw new ProvenanceMarkError("InvalidChainIdLength", void 0, {
|
|
879
1273
|
expected: linkLen,
|
|
880
1274
|
actual: chainId.length
|
|
881
1275
|
});
|
|
882
1276
|
const dateBytes = serializeDate(res, date);
|
|
883
1277
|
const seqBytes = serializeSeq(res, seq);
|
|
884
1278
|
const normalizedDate = deserializeDate(res, dateBytes);
|
|
885
|
-
const infoBytes = info !== void 0 ? (0,
|
|
1279
|
+
const infoBytes = info !== void 0 ? (0, _bcts_dcbor.cborData)(info) : new Uint8Array(0);
|
|
886
1280
|
const hash = ProvenanceMark.makeHash(res, key, nextKey, chainId, seqBytes, dateBytes, infoBytes);
|
|
887
1281
|
return new ProvenanceMark(res, new Uint8Array(key), hash, new Uint8Array(chainId), seqBytes, dateBytes, infoBytes, seq, normalizedDate);
|
|
888
1282
|
}
|
|
@@ -891,7 +1285,7 @@ var BCProvenanceMark = (function(exports, __bcts_dcbor, __noble_hashes_sha256, _
|
|
|
891
1285
|
*/
|
|
892
1286
|
static fromMessage(res, message) {
|
|
893
1287
|
const minLen = fixedLength(res);
|
|
894
|
-
if (message.length < minLen) throw new ProvenanceMarkError(
|
|
1288
|
+
if (message.length < minLen) throw new ProvenanceMarkError("InvalidMessageLength", void 0, {
|
|
895
1289
|
expected: minLen,
|
|
896
1290
|
actual: message.length
|
|
897
1291
|
});
|
|
@@ -912,9 +1306,9 @@ var BCProvenanceMark = (function(exports, __bcts_dcbor, __noble_hashes_sha256, _
|
|
|
912
1306
|
const infoStart = infoRangeStart(res);
|
|
913
1307
|
const infoBytes = payload.slice(infoStart);
|
|
914
1308
|
if (infoBytes.length > 0) try {
|
|
915
|
-
(0,
|
|
1309
|
+
(0, _bcts_dcbor.decodeCbor)(infoBytes);
|
|
916
1310
|
} catch {
|
|
917
|
-
throw new ProvenanceMarkError(
|
|
1311
|
+
throw new ProvenanceMarkError("InvalidInfoCbor");
|
|
918
1312
|
}
|
|
919
1313
|
return new ProvenanceMark(res, new Uint8Array(key), new Uint8Array(hash), new Uint8Array(chainId), new Uint8Array(seqBytes), new Uint8Array(dateBytes), new Uint8Array(infoBytes), seq, date);
|
|
920
1314
|
}
|
|
@@ -929,24 +1323,174 @@ var BCProvenanceMark = (function(exports, __bcts_dcbor, __noble_hashes_sha256, _
|
|
|
929
1323
|
]), linkLength(res));
|
|
930
1324
|
}
|
|
931
1325
|
/**
|
|
932
|
-
*
|
|
1326
|
+
* The 32-byte Mark ID.
|
|
1327
|
+
*
|
|
1328
|
+
* The first `linkLength` bytes are the mark's stored hash. The remaining
|
|
1329
|
+
* bytes come from the mark's fingerprint (SHA-256 of CBOR encoding),
|
|
1330
|
+
* ensuring a full 32-byte value is always available regardless of
|
|
1331
|
+
* resolution.
|
|
1332
|
+
*/
|
|
1333
|
+
id() {
|
|
1334
|
+
const result = new Uint8Array(32);
|
|
1335
|
+
const n = this._hash.length;
|
|
1336
|
+
result.set(this._hash, 0);
|
|
1337
|
+
if (n < 32) {
|
|
1338
|
+
const fp = this.fingerprint();
|
|
1339
|
+
result.set(fp.subarray(0, 32 - n), n);
|
|
1340
|
+
}
|
|
1341
|
+
return result;
|
|
1342
|
+
}
|
|
1343
|
+
/**
|
|
1344
|
+
* The full 32-byte Mark ID as a 64-character hex string.
|
|
1345
|
+
*/
|
|
1346
|
+
idHex() {
|
|
1347
|
+
return bytesToHex(this.id());
|
|
1348
|
+
}
|
|
1349
|
+
/**
|
|
1350
|
+
* The first `wordCount` bytes of the Mark ID as upper-case ByteWords.
|
|
1351
|
+
*
|
|
1352
|
+
* @param wordCount Number of bytes to encode, must be in `4..=32`.
|
|
1353
|
+
* @param prefix If `true`, prepends the provenance-mark prefix character.
|
|
1354
|
+
* @throws if `wordCount` is not in the range `4..=32`.
|
|
1355
|
+
*/
|
|
1356
|
+
idBytewords(wordCount, prefix) {
|
|
1357
|
+
if (!Number.isInteger(wordCount) || wordCount < 4 || wordCount > 32) throw new Error(`word_count must be 4..=32, got ${wordCount}`);
|
|
1358
|
+
const s = (0, _bcts_uniform_resources.encodeToWords)(this.id().subarray(0, wordCount)).toUpperCase();
|
|
1359
|
+
return prefix ? `\u{1F15F} ${s}` : s;
|
|
1360
|
+
}
|
|
1361
|
+
/**
|
|
1362
|
+
* The first `wordCount` bytes of the Mark ID as Bytemoji.
|
|
1363
|
+
*
|
|
1364
|
+
* @param wordCount Number of bytes to encode, must be in `4..=32`.
|
|
1365
|
+
* @param prefix If `true`, prepends the provenance-mark prefix character.
|
|
1366
|
+
* @throws if `wordCount` is not in the range `4..=32`.
|
|
1367
|
+
*/
|
|
1368
|
+
idBytemoji(wordCount, prefix) {
|
|
1369
|
+
if (!Number.isInteger(wordCount) || wordCount < 4 || wordCount > 32) throw new Error(`word_count must be 4..=32, got ${wordCount}`);
|
|
1370
|
+
const s = (0, _bcts_uniform_resources.encodeToBytemojis)(this.id().subarray(0, wordCount)).toUpperCase();
|
|
1371
|
+
return prefix ? `\u{1F15F} ${s}` : s;
|
|
1372
|
+
}
|
|
1373
|
+
/**
|
|
1374
|
+
* The first `wordCount` bytes of the Mark ID as upper-case minimal
|
|
1375
|
+
* ByteWords (2 letters per byte, concatenated without separator).
|
|
1376
|
+
*
|
|
1377
|
+
* @param wordCount Number of bytes to encode, must be in `4..=32`.
|
|
1378
|
+
* @param prefix If `true`, prepends the provenance-mark prefix character.
|
|
1379
|
+
* @throws if `wordCount` is not in the range `4..=32`.
|
|
1380
|
+
*/
|
|
1381
|
+
idBytewordsMinimal(wordCount, prefix) {
|
|
1382
|
+
if (!Number.isInteger(wordCount) || wordCount < 4 || wordCount > 32) throw new Error(`word_count must be 4..=32, got ${wordCount}`);
|
|
1383
|
+
const s = (0, _bcts_uniform_resources.encodeToMinimalBytewords)(this.id().subarray(0, wordCount)).toUpperCase();
|
|
1384
|
+
return prefix ? `\u{1F15F} ${s}` : s;
|
|
1385
|
+
}
|
|
1386
|
+
/**
|
|
1387
|
+
* Legacy 8-character hex identifier — the first 4 bytes of the Mark ID.
|
|
1388
|
+
*
|
|
1389
|
+
* @deprecated Use {@link idHex} for the full 64-char hex, or
|
|
1390
|
+
* `idHex().slice(0, 8)` for this legacy short form. Retained for
|
|
1391
|
+
* backwards compatibility; will be removed in a future alpha.
|
|
933
1392
|
*/
|
|
934
1393
|
identifier() {
|
|
935
|
-
return
|
|
1394
|
+
return this.idHex().slice(0, 8);
|
|
936
1395
|
}
|
|
937
1396
|
/**
|
|
938
|
-
*
|
|
1397
|
+
* Legacy 4-byte upper-case ByteWords identifier.
|
|
1398
|
+
*
|
|
1399
|
+
* @deprecated Equivalent to `idBytewords(4, prefix)`. Retained for
|
|
1400
|
+
* backwards compatibility; will be removed in a future alpha.
|
|
939
1401
|
*/
|
|
940
1402
|
bytewordsIdentifier(prefix) {
|
|
941
|
-
|
|
942
|
-
|
|
1403
|
+
return this.idBytewords(4, prefix);
|
|
1404
|
+
}
|
|
1405
|
+
/**
|
|
1406
|
+
* Legacy 8-letter minimal ByteWords identifier (first+last letter of each
|
|
1407
|
+
* of the 4 ByteWords). Example: "ABLE ACID ALSO APEX" -> "AEADAOAX".
|
|
1408
|
+
*
|
|
1409
|
+
* @deprecated Equivalent to `idBytewordsMinimal(4, prefix)`. Retained
|
|
1410
|
+
* for backwards compatibility; will be removed in a future alpha.
|
|
1411
|
+
*/
|
|
1412
|
+
bytewordsMinimalIdentifier(prefix) {
|
|
1413
|
+
return this.idBytewordsMinimal(4, prefix);
|
|
943
1414
|
}
|
|
944
1415
|
/**
|
|
945
|
-
*
|
|
1416
|
+
* Legacy 4-byte upper-case Bytemoji identifier.
|
|
1417
|
+
*
|
|
1418
|
+
* @deprecated Equivalent to `idBytemoji(4, prefix)`. Retained for
|
|
1419
|
+
* backwards compatibility; will be removed in a future alpha.
|
|
946
1420
|
*/
|
|
947
1421
|
bytemojiIdentifier(prefix) {
|
|
948
|
-
|
|
949
|
-
|
|
1422
|
+
return this.idBytemoji(4, prefix);
|
|
1423
|
+
}
|
|
1424
|
+
/**
|
|
1425
|
+
* Computes the minimum prefix length (in bytes, `4..=32`) each mark needs
|
|
1426
|
+
* so that every mark in the set has a unique Mark ID prefix.
|
|
1427
|
+
*
|
|
1428
|
+
* Non-colliding marks get the minimum of 4. Only marks whose 4-byte
|
|
1429
|
+
* prefixes collide are extended.
|
|
1430
|
+
*/
|
|
1431
|
+
static minimalNoncollidingPrefixLengths(ids) {
|
|
1432
|
+
const n = ids.length;
|
|
1433
|
+
const lengths = new Array(n).fill(4);
|
|
1434
|
+
const groups = /* @__PURE__ */ new Map();
|
|
1435
|
+
for (let i = 0; i < n; i++) {
|
|
1436
|
+
const key = bytesToHex(ids[i].subarray(0, 4));
|
|
1437
|
+
const g = groups.get(key);
|
|
1438
|
+
if (g !== void 0) g.push(i);
|
|
1439
|
+
else groups.set(key, [i]);
|
|
1440
|
+
}
|
|
1441
|
+
for (const indices of groups.values()) {
|
|
1442
|
+
if (indices.length <= 1) continue;
|
|
1443
|
+
ProvenanceMark.resolveCollisionGroup(ids, indices, lengths);
|
|
1444
|
+
}
|
|
1445
|
+
return lengths;
|
|
1446
|
+
}
|
|
1447
|
+
static resolveCollisionGroup(ids, initialIndices, lengths) {
|
|
1448
|
+
let unresolved = [...initialIndices];
|
|
1449
|
+
for (let prefixLen = 5; prefixLen <= 32; prefixLen++) {
|
|
1450
|
+
const subGroups = /* @__PURE__ */ new Map();
|
|
1451
|
+
for (const i of unresolved) {
|
|
1452
|
+
const key = bytesToHex(ids[i].subarray(0, prefixLen));
|
|
1453
|
+
const g = subGroups.get(key);
|
|
1454
|
+
if (g !== void 0) g.push(i);
|
|
1455
|
+
else subGroups.set(key, [i]);
|
|
1456
|
+
}
|
|
1457
|
+
const nextUnresolved = [];
|
|
1458
|
+
for (const subIndices of subGroups.values()) if (subIndices.length === 1) lengths[subIndices[0]] = prefixLen;
|
|
1459
|
+
else nextUnresolved.push(...subIndices);
|
|
1460
|
+
if (nextUnresolved.length === 0) return;
|
|
1461
|
+
unresolved = nextUnresolved;
|
|
1462
|
+
}
|
|
1463
|
+
for (const i of unresolved) lengths[i] = 32;
|
|
1464
|
+
}
|
|
1465
|
+
/**
|
|
1466
|
+
* Returns disambiguated upper-case ByteWords Mark IDs for a set of marks.
|
|
1467
|
+
*
|
|
1468
|
+
* Non-colliding marks get 4-word identifiers. Only marks whose 4-byte
|
|
1469
|
+
* prefixes collide are extended with additional words (up to 32 bytes
|
|
1470
|
+
* per identifier).
|
|
1471
|
+
*/
|
|
1472
|
+
static disambiguatedIdBytewords(marks, prefix) {
|
|
1473
|
+
const ids = marks.map((m) => m.id());
|
|
1474
|
+
const lengths = ProvenanceMark.minimalNoncollidingPrefixLengths(ids);
|
|
1475
|
+
return ids.map((id, i) => {
|
|
1476
|
+
const s = (0, _bcts_uniform_resources.encodeToWords)(id.subarray(0, lengths[i])).toUpperCase();
|
|
1477
|
+
return prefix ? `\u{1F15F} ${s}` : s;
|
|
1478
|
+
});
|
|
1479
|
+
}
|
|
1480
|
+
/**
|
|
1481
|
+
* Returns disambiguated Bytemoji Mark IDs for a set of marks.
|
|
1482
|
+
*
|
|
1483
|
+
* Non-colliding marks get 4-emoji identifiers. Only marks whose 4-byte
|
|
1484
|
+
* prefixes collide are extended with additional emojis (up to 32 bytes
|
|
1485
|
+
* per identifier).
|
|
1486
|
+
*/
|
|
1487
|
+
static disambiguatedIdBytemoji(marks, prefix) {
|
|
1488
|
+
const ids = marks.map((m) => m.id());
|
|
1489
|
+
const lengths = ProvenanceMark.minimalNoncollidingPrefixLengths(ids);
|
|
1490
|
+
return ids.map((id, i) => {
|
|
1491
|
+
const s = (0, _bcts_uniform_resources.encodeToBytemojis)(id.subarray(0, lengths[i])).toUpperCase();
|
|
1492
|
+
return prefix ? `\u{1F15F} ${s}` : s;
|
|
1493
|
+
});
|
|
950
1494
|
}
|
|
951
1495
|
/**
|
|
952
1496
|
* Check if this mark precedes another mark in the chain.
|
|
@@ -961,18 +1505,39 @@ var BCProvenanceMark = (function(exports, __bcts_dcbor, __noble_hashes_sha256, _
|
|
|
961
1505
|
}
|
|
962
1506
|
/**
|
|
963
1507
|
* Check if this mark precedes another mark, throwing on validation errors.
|
|
1508
|
+
* Errors carry a structured `validationIssue` in their details, matching Rust's
|
|
1509
|
+
* `Error::Validation(ValidationIssue)` pattern.
|
|
964
1510
|
*/
|
|
965
1511
|
precedesOpt(next) {
|
|
966
|
-
if (next._seq === 0) throw new ProvenanceMarkError(
|
|
967
|
-
if (arraysEqual(next._key, next._chainId)) throw new ProvenanceMarkError(
|
|
968
|
-
if (this._seq !== next._seq - 1)
|
|
969
|
-
|
|
1512
|
+
if (next._seq === 0) throw new ProvenanceMarkError("ValidationError", "non-genesis mark at sequence 0", { validationIssue: { type: "NonGenesisAtZero" } });
|
|
1513
|
+
if (arraysEqual(next._key, next._chainId)) throw new ProvenanceMarkError("ValidationError", "genesis mark must have key equal to chain_id", { validationIssue: { type: "InvalidGenesisKey" } });
|
|
1514
|
+
if (this._seq !== next._seq - 1) {
|
|
1515
|
+
const issue = {
|
|
1516
|
+
type: "SequenceGap",
|
|
1517
|
+
expected: this._seq + 1,
|
|
1518
|
+
actual: next._seq
|
|
1519
|
+
};
|
|
1520
|
+
throw new ProvenanceMarkError("ValidationError", `sequence gap: expected ${this._seq + 1}, got ${next._seq}`, { validationIssue: issue });
|
|
1521
|
+
}
|
|
1522
|
+
if (this._date > next._date) {
|
|
1523
|
+
const dateStr = dateToDisplay(this._date);
|
|
1524
|
+
const nextDateStr = dateToDisplay(next._date);
|
|
1525
|
+
const issue = {
|
|
1526
|
+
type: "DateOrdering",
|
|
1527
|
+
previous: dateStr,
|
|
1528
|
+
next: nextDateStr
|
|
1529
|
+
};
|
|
1530
|
+
throw new ProvenanceMarkError("ValidationError", `date ordering: ${dateStr} > ${nextDateStr}`, { validationIssue: issue });
|
|
1531
|
+
}
|
|
970
1532
|
const expectedHash = ProvenanceMark.makeHash(this._res, this._key, next._key, this._chainId, this._seqBytes, this._dateBytes, this._infoBytes);
|
|
971
|
-
if (!arraysEqual(this._hash, expectedHash))
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
1533
|
+
if (!arraysEqual(this._hash, expectedHash)) {
|
|
1534
|
+
const issue = {
|
|
1535
|
+
type: "HashMismatch",
|
|
1536
|
+
expected: bytesToHex(expectedHash),
|
|
1537
|
+
actual: bytesToHex(this._hash)
|
|
1538
|
+
};
|
|
1539
|
+
throw new ProvenanceMarkError("ValidationError", `hash mismatch: expected ${bytesToHex(expectedHash)}, got ${bytesToHex(this._hash)}`, { validationIssue: issue });
|
|
1540
|
+
}
|
|
976
1541
|
}
|
|
977
1542
|
/**
|
|
978
1543
|
* Check if a sequence of marks is valid.
|
|
@@ -993,35 +1558,61 @@ var BCProvenanceMark = (function(exports, __bcts_dcbor, __noble_hashes_sha256, _
|
|
|
993
1558
|
* Encode as bytewords with the given style.
|
|
994
1559
|
*/
|
|
995
1560
|
toBytewordsWithStyle(style) {
|
|
996
|
-
return (0,
|
|
1561
|
+
return (0, _bcts_uniform_resources.encodeBytewords)(this.message(), style);
|
|
997
1562
|
}
|
|
998
1563
|
/**
|
|
999
1564
|
* Encode as standard bytewords.
|
|
1000
1565
|
*/
|
|
1001
1566
|
toBytewords() {
|
|
1002
|
-
return this.toBytewordsWithStyle(
|
|
1567
|
+
return this.toBytewordsWithStyle(_bcts_uniform_resources.BytewordsStyle.Standard);
|
|
1003
1568
|
}
|
|
1004
1569
|
/**
|
|
1005
1570
|
* Decode from bytewords.
|
|
1006
1571
|
*/
|
|
1007
1572
|
static fromBytewords(res, bytewords) {
|
|
1008
|
-
const message = (0,
|
|
1573
|
+
const message = (0, _bcts_uniform_resources.decodeBytewords)(bytewords, _bcts_uniform_resources.BytewordsStyle.Standard);
|
|
1009
1574
|
return ProvenanceMark.fromMessage(res, message);
|
|
1010
1575
|
}
|
|
1011
1576
|
/**
|
|
1012
|
-
* Encode for URL (minimal bytewords of CBOR).
|
|
1577
|
+
* Encode for URL (minimal bytewords of tagged CBOR).
|
|
1013
1578
|
*/
|
|
1014
1579
|
toUrlEncoding() {
|
|
1015
|
-
return (0,
|
|
1580
|
+
return (0, _bcts_uniform_resources.encodeBytewords)(this.toCborData(), _bcts_uniform_resources.BytewordsStyle.Minimal);
|
|
1016
1581
|
}
|
|
1017
1582
|
/**
|
|
1018
1583
|
* Decode from URL encoding.
|
|
1019
1584
|
*/
|
|
1020
1585
|
static fromUrlEncoding(urlEncoding) {
|
|
1021
|
-
const cborValue = (0,
|
|
1586
|
+
const cborValue = (0, _bcts_dcbor.decodeCbor)((0, _bcts_uniform_resources.decodeBytewords)(urlEncoding, _bcts_uniform_resources.BytewordsStyle.Minimal));
|
|
1022
1587
|
return ProvenanceMark.fromTaggedCbor(cborValue);
|
|
1023
1588
|
}
|
|
1024
1589
|
/**
|
|
1590
|
+
* Returns the {@link UR} representation of this mark (untagged CBOR
|
|
1591
|
+
* with type `"provenance"`).
|
|
1592
|
+
*
|
|
1593
|
+
* Mirrors Rust `UREncodable::ur()` for `ProvenanceMark` — the
|
|
1594
|
+
* blanket impl on `CBORTaggedEncodable` produces a UR whose
|
|
1595
|
+
* payload is the *untagged* CBOR (the type name itself stands in
|
|
1596
|
+
* for the tag). See `bc-ur-rust/src/ur_encodable.rs:8-18`.
|
|
1597
|
+
*/
|
|
1598
|
+
ur() {
|
|
1599
|
+
return _bcts_uniform_resources.UR.new("provenance", this.untaggedCbor());
|
|
1600
|
+
}
|
|
1601
|
+
/**
|
|
1602
|
+
* Get the UR string representation (e.g., "ur:provenance/...").
|
|
1603
|
+
*/
|
|
1604
|
+
urString() {
|
|
1605
|
+
return this.ur().string();
|
|
1606
|
+
}
|
|
1607
|
+
/**
|
|
1608
|
+
* Create from a UR string.
|
|
1609
|
+
*/
|
|
1610
|
+
static fromURString(urString) {
|
|
1611
|
+
const ur = _bcts_uniform_resources.UR.fromURString(urString);
|
|
1612
|
+
if (ur.urTypeStr() !== "provenance") throw new ProvenanceMarkError("CborError", void 0, { message: `Expected UR type 'provenance', got '${ur.urTypeStr()}'` });
|
|
1613
|
+
return ProvenanceMark.fromUntaggedCbor(ur.cbor());
|
|
1614
|
+
}
|
|
1615
|
+
/**
|
|
1025
1616
|
* Build a URL with this mark as a query parameter.
|
|
1026
1617
|
*/
|
|
1027
1618
|
toUrl(base) {
|
|
@@ -1034,21 +1625,21 @@ var BCProvenanceMark = (function(exports, __bcts_dcbor, __noble_hashes_sha256, _
|
|
|
1034
1625
|
*/
|
|
1035
1626
|
static fromUrl(url) {
|
|
1036
1627
|
const param = url.searchParams.get("provenance");
|
|
1037
|
-
if (param === null || param === "") throw new ProvenanceMarkError(
|
|
1628
|
+
if (param === null || param === "") throw new ProvenanceMarkError("MissingUrlParameter", void 0, { parameter: "provenance" });
|
|
1038
1629
|
return ProvenanceMark.fromUrlEncoding(param);
|
|
1039
1630
|
}
|
|
1040
1631
|
/**
|
|
1041
1632
|
* Get the untagged CBOR representation.
|
|
1042
1633
|
*/
|
|
1043
1634
|
untaggedCbor() {
|
|
1044
|
-
return (0,
|
|
1635
|
+
return (0, _bcts_dcbor.cbor)([resolutionToCbor(this._res), (0, _bcts_dcbor.cbor)(this.message())]);
|
|
1045
1636
|
}
|
|
1046
1637
|
/**
|
|
1047
1638
|
* Get the tagged CBOR representation.
|
|
1048
1639
|
*/
|
|
1049
1640
|
taggedCbor() {
|
|
1050
|
-
return (0,
|
|
1051
|
-
tag:
|
|
1641
|
+
return (0, _bcts_dcbor.cbor)({
|
|
1642
|
+
tag: _bcts_tags.PROVENANCE_MARK.value,
|
|
1052
1643
|
value: this.untaggedCbor()
|
|
1053
1644
|
});
|
|
1054
1645
|
}
|
|
@@ -1056,16 +1647,16 @@ var BCProvenanceMark = (function(exports, __bcts_dcbor, __noble_hashes_sha256, _
|
|
|
1056
1647
|
* Serialize to CBOR bytes (tagged).
|
|
1057
1648
|
*/
|
|
1058
1649
|
toCborData() {
|
|
1059
|
-
return (0,
|
|
1650
|
+
return (0, _bcts_dcbor.cborData)(this.taggedCbor());
|
|
1060
1651
|
}
|
|
1061
1652
|
/**
|
|
1062
1653
|
* Create from untagged CBOR.
|
|
1063
1654
|
*/
|
|
1064
1655
|
static fromUntaggedCbor(cborValue) {
|
|
1065
|
-
const arr = (0,
|
|
1066
|
-
if (arr.length !== 2) throw new ProvenanceMarkError(
|
|
1656
|
+
const arr = (0, _bcts_dcbor.expectArray)(cborValue);
|
|
1657
|
+
if (arr.length !== 2) throw new ProvenanceMarkError("CborError", void 0, { message: "Invalid provenance mark length" });
|
|
1067
1658
|
const res = resolutionFromCbor(arr[0]);
|
|
1068
|
-
const message = (0,
|
|
1659
|
+
const message = (0, _bcts_dcbor.expectBytes)(arr[1]);
|
|
1069
1660
|
return ProvenanceMark.fromMessage(res, message);
|
|
1070
1661
|
}
|
|
1071
1662
|
/**
|
|
@@ -1073,15 +1664,15 @@ var BCProvenanceMark = (function(exports, __bcts_dcbor, __noble_hashes_sha256, _
|
|
|
1073
1664
|
*/
|
|
1074
1665
|
static fromTaggedCbor(cborValue) {
|
|
1075
1666
|
const cborObj = cborValue;
|
|
1076
|
-
if (cborObj.tag !==
|
|
1077
|
-
if (cborObj.value === void 0) throw new ProvenanceMarkError(
|
|
1667
|
+
if (cborObj.tag !== _bcts_tags.PROVENANCE_MARK.value) throw new ProvenanceMarkError("CborError", void 0, { message: `Expected tag ${_bcts_tags.PROVENANCE_MARK.value}, got ${String(cborObj.tag)}` });
|
|
1668
|
+
if (cborObj.value === void 0) throw new ProvenanceMarkError("CborError", void 0, { message: "Tagged CBOR value is missing" });
|
|
1078
1669
|
return ProvenanceMark.fromUntaggedCbor(cborObj.value);
|
|
1079
1670
|
}
|
|
1080
1671
|
/**
|
|
1081
1672
|
* Create from CBOR bytes.
|
|
1082
1673
|
*/
|
|
1083
1674
|
static fromCborData(data) {
|
|
1084
|
-
const cborValue = (0,
|
|
1675
|
+
const cborValue = (0, _bcts_dcbor.decodeCbor)(data);
|
|
1085
1676
|
return ProvenanceMark.fromTaggedCbor(cborValue);
|
|
1086
1677
|
}
|
|
1087
1678
|
/**
|
|
@@ -1092,23 +1683,42 @@ var BCProvenanceMark = (function(exports, __bcts_dcbor, __noble_hashes_sha256, _
|
|
|
1092
1683
|
}
|
|
1093
1684
|
/**
|
|
1094
1685
|
* Debug string representation.
|
|
1686
|
+
*
|
|
1687
|
+
* As of provenance-mark v0.24, this includes the full 64-character Mark ID
|
|
1688
|
+
* hex (matching rust's `Display` impl). Pre-v0.24 callers that depended on
|
|
1689
|
+
* the 8-character prefix should use `idHex().slice(0, 8)` directly.
|
|
1095
1690
|
*/
|
|
1096
1691
|
toString() {
|
|
1097
|
-
return `ProvenanceMark(${this.
|
|
1692
|
+
return `ProvenanceMark(${this.idHex()})`;
|
|
1098
1693
|
}
|
|
1099
1694
|
/**
|
|
1100
1695
|
* Detailed debug representation.
|
|
1696
|
+
*
|
|
1697
|
+
* Mirrors Rust `Mark::Debug` exactly: every field is rendered
|
|
1698
|
+
* Rust-style (hex bytes for keys/hashes/IDs, plain integer for
|
|
1699
|
+
* `seq`, `Date::Display` for the date). The Low-resolution test
|
|
1700
|
+
* vector in Rust `tests/mark.rs::test_low_resolution` ends with
|
|
1701
|
+
* `date: 2023-06-20` — i.e. midnight-UTC dates are rendered without
|
|
1702
|
+
* a time suffix. We use {@link dateToDisplay} to mirror that
|
|
1703
|
+
* exactly; earlier revisions of this port stripped just the
|
|
1704
|
+
* `.000Z` fractional component, which left `2023-06-20T00:00:00Z`
|
|
1705
|
+
* and broke the Low-resolution debug-string parity.
|
|
1101
1706
|
*/
|
|
1102
1707
|
toDebugString() {
|
|
1708
|
+
const dateStr = dateToDisplay(this._date);
|
|
1103
1709
|
const components = [
|
|
1104
1710
|
`key: ${bytesToHex(this._key)}`,
|
|
1105
1711
|
`hash: ${bytesToHex(this._hash)}`,
|
|
1106
1712
|
`chainID: ${bytesToHex(this._chainId)}`,
|
|
1107
1713
|
`seq: ${this._seq}`,
|
|
1108
|
-
`date: ${
|
|
1714
|
+
`date: ${dateStr}`
|
|
1109
1715
|
];
|
|
1110
1716
|
const info = this.info();
|
|
1111
|
-
if (info !== void 0)
|
|
1717
|
+
if (info !== void 0) {
|
|
1718
|
+
const textValue = info.asText();
|
|
1719
|
+
if (textValue !== void 0) components.push(`info: "${textValue}"`);
|
|
1720
|
+
else components.push(`info: ${info.toDiagnostic()}`);
|
|
1721
|
+
}
|
|
1112
1722
|
return `ProvenanceMark(${components.join(", ")})`;
|
|
1113
1723
|
}
|
|
1114
1724
|
/**
|
|
@@ -1118,16 +1728,20 @@ var BCProvenanceMark = (function(exports, __bcts_dcbor, __noble_hashes_sha256, _
|
|
|
1118
1728
|
return this._res === other._res && arraysEqual(this.message(), other.message());
|
|
1119
1729
|
}
|
|
1120
1730
|
/**
|
|
1121
|
-
* JSON serialization.
|
|
1731
|
+
* JSON serialization. Field order, names, and date format mirror Rust's
|
|
1732
|
+
* `#[derive(Serialize)]` on `ProvenanceMark` (provenance-mark-rust/src/mark.rs):
|
|
1733
|
+
* `seq, date, res, chain_id, key, hash[, info_bytes]`. The date uses
|
|
1734
|
+
* `dateToDisplay()` (date-only when midnight, RFC3339-seconds with `Z`
|
|
1735
|
+
* otherwise), matching Rust's `serialize_iso8601` / `Date::to_string()`.
|
|
1122
1736
|
*/
|
|
1123
1737
|
toJSON() {
|
|
1124
1738
|
const result = {
|
|
1739
|
+
seq: this._seq,
|
|
1740
|
+
date: dateToDisplay(this._date),
|
|
1125
1741
|
res: this._res,
|
|
1742
|
+
chain_id: toBase64(this._chainId),
|
|
1126
1743
|
key: toBase64(this._key),
|
|
1127
|
-
hash: toBase64(this._hash)
|
|
1128
|
-
chainID: toBase64(this._chainId),
|
|
1129
|
-
seq: this._seq,
|
|
1130
|
-
date: this._date.toISOString()
|
|
1744
|
+
hash: toBase64(this._hash)
|
|
1131
1745
|
};
|
|
1132
1746
|
if (this._infoBytes.length > 0) result["info_bytes"] = toBase64(this._infoBytes);
|
|
1133
1747
|
return result;
|
|
@@ -1139,16 +1753,55 @@ var BCProvenanceMark = (function(exports, __bcts_dcbor, __noble_hashes_sha256, _
|
|
|
1139
1753
|
const res = json["res"];
|
|
1140
1754
|
const key = fromBase64(json["key"]);
|
|
1141
1755
|
const hash = fromBase64(json["hash"]);
|
|
1142
|
-
const chainId = fromBase64(json["chainID"]);
|
|
1756
|
+
const chainId = fromBase64(json["chain_id"] ?? json["chainID"]);
|
|
1143
1757
|
const seq = json["seq"];
|
|
1144
1758
|
const dateStr = json["date"];
|
|
1145
1759
|
const date = new Date(dateStr);
|
|
1146
1760
|
const seqBytes = serializeSeq(res, seq);
|
|
1147
1761
|
const dateBytes = serializeDate(res, date);
|
|
1148
1762
|
let infoBytes = new Uint8Array(0);
|
|
1149
|
-
if (typeof json["info_bytes"] === "string")
|
|
1763
|
+
if (typeof json["info_bytes"] === "string") {
|
|
1764
|
+
infoBytes = fromBase64(json["info_bytes"]);
|
|
1765
|
+
if (infoBytes.length > 0) try {
|
|
1766
|
+
(0, _bcts_dcbor.decodeCbor)(infoBytes);
|
|
1767
|
+
} catch (e) {
|
|
1768
|
+
throw new ProvenanceMarkError("CborError", "info_bytes is not valid CBOR", { details: e instanceof Error ? e.message : String(e) });
|
|
1769
|
+
}
|
|
1770
|
+
}
|
|
1150
1771
|
return new ProvenanceMark(res, key, hash, chainId, seqBytes, dateBytes, infoBytes, seq, date);
|
|
1151
1772
|
}
|
|
1773
|
+
/**
|
|
1774
|
+
* Validate a collection of provenance marks.
|
|
1775
|
+
*
|
|
1776
|
+
* Matches Rust: `ProvenanceMark::validate()` which delegates to
|
|
1777
|
+
* `ValidationReport::validate()`.
|
|
1778
|
+
*/
|
|
1779
|
+
static validate(marks) {
|
|
1780
|
+
return validate(marks);
|
|
1781
|
+
}
|
|
1782
|
+
/**
|
|
1783
|
+
* Convert this provenance mark to a Gordian Envelope.
|
|
1784
|
+
*
|
|
1785
|
+
* Creates a leaf envelope containing the tagged CBOR representation.
|
|
1786
|
+
* Matches Rust: `Envelope::new(mark.to_cbor())` which creates a CBOR leaf.
|
|
1787
|
+
*/
|
|
1788
|
+
intoEnvelope() {
|
|
1789
|
+
return _bcts_envelope.Envelope.newLeaf(this.taggedCbor());
|
|
1790
|
+
}
|
|
1791
|
+
/**
|
|
1792
|
+
* Extract a ProvenanceMark from a Gordian Envelope.
|
|
1793
|
+
*
|
|
1794
|
+
* Matches Rust: `envelope.subject().try_leaf()?.try_into()`
|
|
1795
|
+
*
|
|
1796
|
+
* @param envelope - The envelope to extract from
|
|
1797
|
+
* @returns The extracted provenance mark
|
|
1798
|
+
* @throws ProvenanceMarkError if extraction fails
|
|
1799
|
+
*/
|
|
1800
|
+
static fromEnvelope(envelope) {
|
|
1801
|
+
const leaf = envelope.subject().asLeaf();
|
|
1802
|
+
if (leaf !== void 0) return ProvenanceMark.fromTaggedCbor(leaf);
|
|
1803
|
+
throw new ProvenanceMarkError("CborError", void 0, { message: "Could not extract ProvenanceMark from envelope" });
|
|
1804
|
+
}
|
|
1152
1805
|
};
|
|
1153
1806
|
/**
|
|
1154
1807
|
* Helper function to compare two Uint8Arrays.
|
|
@@ -1158,10 +1811,14 @@ var BCProvenanceMark = (function(exports, __bcts_dcbor, __noble_hashes_sha256, _
|
|
|
1158
1811
|
for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
|
|
1159
1812
|
return true;
|
|
1160
1813
|
}
|
|
1161
|
-
|
|
1162
|
-
//#
|
|
1163
|
-
|
|
1164
|
-
|
|
1814
|
+
//#endregion
|
|
1815
|
+
//#region src/generator.ts
|
|
1816
|
+
/**
|
|
1817
|
+
* Copyright © 2023-2026 Blockchain Commons, LLC
|
|
1818
|
+
* Copyright © 2025-2026 Parity Technologies
|
|
1819
|
+
*
|
|
1820
|
+
*/
|
|
1821
|
+
/**
|
|
1165
1822
|
* Generator for creating provenance mark chains.
|
|
1166
1823
|
*/
|
|
1167
1824
|
var ProvenanceMarkGenerator = class ProvenanceMarkGenerator {
|
|
@@ -1211,8 +1868,8 @@ var BCProvenanceMark = (function(exports, __bcts_dcbor, __noble_hashes_sha256, _
|
|
|
1211
1868
|
/**
|
|
1212
1869
|
* Create a new generator with custom random data.
|
|
1213
1870
|
*/
|
|
1214
|
-
static newUsing(res, randomData
|
|
1215
|
-
const seed = ProvenanceSeed.newUsing(randomData
|
|
1871
|
+
static newUsing(res, randomData) {
|
|
1872
|
+
const seed = ProvenanceSeed.newUsing(randomData);
|
|
1216
1873
|
return ProvenanceMarkGenerator.newWithSeed(res, seed);
|
|
1217
1874
|
}
|
|
1218
1875
|
/**
|
|
@@ -1227,7 +1884,7 @@ var BCProvenanceMark = (function(exports, __bcts_dcbor, __noble_hashes_sha256, _
|
|
|
1227
1884
|
*/
|
|
1228
1885
|
static new(res, seed, chainId, nextSeq, rngState) {
|
|
1229
1886
|
const linkLen = linkLength(res);
|
|
1230
|
-
if (chainId.length !== linkLen) throw new ProvenanceMarkError(
|
|
1887
|
+
if (chainId.length !== linkLen) throw new ProvenanceMarkError("InvalidChainIdLength", void 0, {
|
|
1231
1888
|
expected: linkLen,
|
|
1232
1889
|
actual: chainId.length
|
|
1233
1890
|
});
|
|
@@ -1253,9 +1910,24 @@ var BCProvenanceMark = (function(exports, __bcts_dcbor, __noble_hashes_sha256, _
|
|
|
1253
1910
|
}
|
|
1254
1911
|
/**
|
|
1255
1912
|
* String representation.
|
|
1913
|
+
*
|
|
1914
|
+
* Mirrors Rust `Display for ProvenanceMarkGenerator`
|
|
1915
|
+
* (`provenance-mark-rust/src/generator.rs:135-147`):
|
|
1916
|
+
*
|
|
1917
|
+
* ```rust
|
|
1918
|
+
* write!(f, "ProvenanceMarkGenerator(chainID: {}, res: {}, seed: {}, nextSeq: {}, rngState: {:?})",
|
|
1919
|
+
* hex::encode(&self.chain_id), self.res, self.seed.hex(), self.next_seq, self.rng_state)
|
|
1920
|
+
* ```
|
|
1921
|
+
*
|
|
1922
|
+
* The `rngState` field uses Rust's `{:?}` (Debug) format, which on a
|
|
1923
|
+
* `RngState([u8; 32])` tuple struct produces `RngState([n0, n1, ...])`
|
|
1924
|
+
* with each byte rendered as a decimal integer. Earlier revisions of
|
|
1925
|
+
* this port omitted `rngState` entirely from `toString()`, so the
|
|
1926
|
+
* output diverged from Rust's `Display`.
|
|
1256
1927
|
*/
|
|
1257
1928
|
toString() {
|
|
1258
|
-
|
|
1929
|
+
const rngBytes = Array.from(this._rngState.toBytes()).join(", ");
|
|
1930
|
+
return `ProvenanceMarkGenerator(chainID: ${bytesToHex(this._chainId)}, res: ${this._res}, seed: ${this._seed.hex()}, nextSeq: ${this._nextSeq}, rngState: RngState([${rngBytes}]))`;
|
|
1259
1931
|
}
|
|
1260
1932
|
/**
|
|
1261
1933
|
* JSON serialization.
|
|
@@ -1280,281 +1952,67 @@ var BCProvenanceMark = (function(exports, __bcts_dcbor, __noble_hashes_sha256, _
|
|
|
1280
1952
|
const rngState = RngState.fromBytes(fromBase64(json["rngState"]));
|
|
1281
1953
|
return ProvenanceMarkGenerator.new(res, seed, chainId, nextSeq, rngState);
|
|
1282
1954
|
}
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
/**
|
|
1329
|
-
* Check if the validation report contains interesting information.
|
|
1330
|
-
*/
|
|
1331
|
-
function isInteresting(report) {
|
|
1332
|
-
if (report.chains.length === 0) return false;
|
|
1333
|
-
for (const chain of report.chains) if (!chain.hasGenesis) return true;
|
|
1334
|
-
if (report.chains.length === 1) {
|
|
1335
|
-
const chain = report.chains[0];
|
|
1336
|
-
if (chain.sequences.length === 1) {
|
|
1337
|
-
if (chain.sequences[0].marks.every((m) => m.issues.length === 0)) return false;
|
|
1338
|
-
}
|
|
1339
|
-
}
|
|
1340
|
-
return true;
|
|
1341
|
-
}
|
|
1342
|
-
/**
|
|
1343
|
-
* Format the validation report as human-readable text.
|
|
1344
|
-
*/
|
|
1345
|
-
function formatText(report) {
|
|
1346
|
-
if (!isInteresting(report)) return "";
|
|
1347
|
-
const lines = [];
|
|
1348
|
-
lines.push(`Total marks: ${report.marks.length}`);
|
|
1349
|
-
lines.push(`Chains: ${report.chains.length}`);
|
|
1350
|
-
lines.push("");
|
|
1351
|
-
for (let chainIdx = 0; chainIdx < report.chains.length; chainIdx++) {
|
|
1352
|
-
const chain = report.chains[chainIdx];
|
|
1353
|
-
const chainIdStr = chainIdHex(chain);
|
|
1354
|
-
const shortChainId = chainIdStr.length > 8 ? chainIdStr.slice(0, 8) : chainIdStr;
|
|
1355
|
-
lines.push(`Chain ${chainIdx + 1}: ${shortChainId}`);
|
|
1356
|
-
if (!chain.hasGenesis) lines.push(" Warning: No genesis mark found");
|
|
1357
|
-
for (const seq of chain.sequences) for (const flaggedMark of seq.marks) {
|
|
1358
|
-
const mark = flaggedMark.mark;
|
|
1359
|
-
const shortId = mark.identifier();
|
|
1360
|
-
const seqNum = mark.seq();
|
|
1361
|
-
const annotations = [];
|
|
1362
|
-
if (mark.isGenesis()) annotations.push("genesis mark");
|
|
1363
|
-
for (const issue of flaggedMark.issues) {
|
|
1364
|
-
let issueStr;
|
|
1365
|
-
switch (issue.type) {
|
|
1366
|
-
case "SequenceGap":
|
|
1367
|
-
issueStr = `gap: ${issue.expected} missing`;
|
|
1368
|
-
break;
|
|
1369
|
-
case "DateOrdering":
|
|
1370
|
-
issueStr = `date ${issue.previous} < ${issue.next}`;
|
|
1371
|
-
break;
|
|
1372
|
-
case "HashMismatch":
|
|
1373
|
-
issueStr = "hash mismatch";
|
|
1374
|
-
break;
|
|
1375
|
-
case "KeyMismatch":
|
|
1376
|
-
issueStr = "key mismatch";
|
|
1377
|
-
break;
|
|
1378
|
-
case "NonGenesisAtZero":
|
|
1379
|
-
issueStr = "non-genesis at seq 0";
|
|
1380
|
-
break;
|
|
1381
|
-
case "InvalidGenesisKey":
|
|
1382
|
-
issueStr = "invalid genesis key";
|
|
1383
|
-
break;
|
|
1384
|
-
}
|
|
1385
|
-
annotations.push(issueStr);
|
|
1386
|
-
}
|
|
1387
|
-
if (annotations.length === 0) lines.push(` ${seqNum}: ${shortId}`);
|
|
1388
|
-
else lines.push(` ${seqNum}: ${shortId} (${annotations.join(", ")})`);
|
|
1389
|
-
}
|
|
1390
|
-
lines.push("");
|
|
1391
|
-
}
|
|
1392
|
-
return lines.join("\n").trimEnd();
|
|
1393
|
-
}
|
|
1394
|
-
/**
|
|
1395
|
-
* Format the validation report.
|
|
1396
|
-
*/
|
|
1397
|
-
function formatReport(report, format) {
|
|
1398
|
-
switch (format) {
|
|
1399
|
-
case ValidationReportFormat.Text: return formatText(report);
|
|
1400
|
-
case ValidationReportFormat.JsonCompact: return JSON.stringify(reportToJSON(report));
|
|
1401
|
-
case ValidationReportFormat.JsonPretty: return JSON.stringify(reportToJSON(report), null, 2);
|
|
1402
|
-
}
|
|
1403
|
-
}
|
|
1404
|
-
/**
|
|
1405
|
-
* Convert a report to a JSON-serializable object.
|
|
1406
|
-
*/
|
|
1407
|
-
function reportToJSON(report) {
|
|
1408
|
-
return {
|
|
1409
|
-
marks: report.marks.map((m) => m.toUrlEncoding()),
|
|
1410
|
-
chains: report.chains.map((chain) => ({
|
|
1411
|
-
chain_id: hexEncode(chain.chainId),
|
|
1412
|
-
has_genesis: chain.hasGenesis,
|
|
1413
|
-
marks: chain.marks.map((m) => m.toUrlEncoding()),
|
|
1414
|
-
sequences: chain.sequences.map((seq) => ({
|
|
1415
|
-
start_seq: seq.startSeq,
|
|
1416
|
-
end_seq: seq.endSeq,
|
|
1417
|
-
marks: seq.marks.map((fm) => ({
|
|
1418
|
-
mark: fm.mark.toUrlEncoding(),
|
|
1419
|
-
issues: fm.issues
|
|
1420
|
-
}))
|
|
1421
|
-
}))
|
|
1422
|
-
}))
|
|
1423
|
-
};
|
|
1424
|
-
}
|
|
1425
|
-
/**
|
|
1426
|
-
* Build sequence bins for a chain.
|
|
1427
|
-
*/
|
|
1428
|
-
function buildSequenceBins(marks) {
|
|
1429
|
-
const sequences = [];
|
|
1430
|
-
let currentSequence = [];
|
|
1431
|
-
for (let i = 0; i < marks.length; i++) {
|
|
1432
|
-
const mark = marks[i];
|
|
1433
|
-
if (i === 0) currentSequence.push({
|
|
1434
|
-
mark,
|
|
1435
|
-
issues: []
|
|
1436
|
-
});
|
|
1437
|
-
else {
|
|
1438
|
-
const prev = marks[i - 1];
|
|
1439
|
-
try {
|
|
1440
|
-
prev.precedesOpt(mark);
|
|
1441
|
-
currentSequence.push({
|
|
1442
|
-
mark,
|
|
1443
|
-
issues: []
|
|
1444
|
-
});
|
|
1445
|
-
} catch (e) {
|
|
1446
|
-
if (currentSequence.length > 0) sequences.push(createSequenceReport(currentSequence));
|
|
1447
|
-
currentSequence = [{
|
|
1448
|
-
mark,
|
|
1449
|
-
issues: [parseValidationError(e, prev, mark)]
|
|
1450
|
-
}];
|
|
1451
|
-
}
|
|
1452
|
-
}
|
|
1453
|
-
}
|
|
1454
|
-
if (currentSequence.length > 0) sequences.push(createSequenceReport(currentSequence));
|
|
1455
|
-
return sequences;
|
|
1456
|
-
}
|
|
1457
|
-
/**
|
|
1458
|
-
* Parse a validation error into a ValidationIssue.
|
|
1459
|
-
*/
|
|
1460
|
-
function parseValidationError(e, prev, next) {
|
|
1461
|
-
const message = e instanceof Error ? e.message : "";
|
|
1462
|
-
if (message !== "" && message.includes("non-genesis mark at sequence 0")) return { type: "NonGenesisAtZero" };
|
|
1463
|
-
if (message !== "" && message.includes("genesis mark must have key equal to chain_id")) return { type: "InvalidGenesisKey" };
|
|
1464
|
-
if (message !== "" && message.includes("sequence gap")) {
|
|
1465
|
-
const match = /expected (\d+), got (\d+)/.exec(message);
|
|
1466
|
-
if (match !== null) return {
|
|
1467
|
-
type: "SequenceGap",
|
|
1468
|
-
expected: parseInt(match[1], 10),
|
|
1469
|
-
actual: parseInt(match[2], 10)
|
|
1470
|
-
};
|
|
1471
|
-
}
|
|
1472
|
-
if (message !== "" && message.includes("date ordering")) return {
|
|
1473
|
-
type: "DateOrdering",
|
|
1474
|
-
previous: prev.date().toISOString(),
|
|
1475
|
-
next: next.date().toISOString()
|
|
1476
|
-
};
|
|
1477
|
-
if (message !== "" && message.includes("hash mismatch")) {
|
|
1478
|
-
const match = /expected: (\w+), actual: (\w+)/.exec(message);
|
|
1479
|
-
if (match !== null) return {
|
|
1480
|
-
type: "HashMismatch",
|
|
1481
|
-
expected: match[1],
|
|
1482
|
-
actual: match[2]
|
|
1483
|
-
};
|
|
1484
|
-
return {
|
|
1485
|
-
type: "HashMismatch",
|
|
1486
|
-
expected: "",
|
|
1487
|
-
actual: ""
|
|
1955
|
+
/**
|
|
1956
|
+
* Convert this generator to a Gordian Envelope.
|
|
1957
|
+
*
|
|
1958
|
+
* The envelope contains structured assertions for all generator fields:
|
|
1959
|
+
* - isA: "provenance-generator"
|
|
1960
|
+
* - res: The resolution
|
|
1961
|
+
* - seed: The seed
|
|
1962
|
+
* - next-seq: The next sequence number
|
|
1963
|
+
* - rng-state: The RNG state
|
|
1964
|
+
*
|
|
1965
|
+
* Note: Use provenanceMarkGeneratorToEnvelope() for a standalone function alternative.
|
|
1966
|
+
*/
|
|
1967
|
+
intoEnvelope() {
|
|
1968
|
+
let envelope = _bcts_envelope.Envelope.new(this._chainId);
|
|
1969
|
+
envelope = envelope.addType("provenance-generator");
|
|
1970
|
+
envelope = envelope.addAssertion("res", resolutionToNumber(this._res));
|
|
1971
|
+
envelope = envelope.addAssertion("seed", this._seed.toBytes());
|
|
1972
|
+
envelope = envelope.addAssertion("next-seq", this._nextSeq);
|
|
1973
|
+
envelope = envelope.addAssertion("rng-state", this._rngState.toBytes());
|
|
1974
|
+
return envelope;
|
|
1975
|
+
}
|
|
1976
|
+
/**
|
|
1977
|
+
* Extract a ProvenanceMarkGenerator from a Gordian Envelope.
|
|
1978
|
+
*
|
|
1979
|
+
* @param envelope - The envelope to extract from
|
|
1980
|
+
* @returns The extracted generator
|
|
1981
|
+
* @throws ProvenanceMarkError if extraction fails
|
|
1982
|
+
*/
|
|
1983
|
+
static fromEnvelope(envelope) {
|
|
1984
|
+
const env = envelope;
|
|
1985
|
+
if (!env.hasType("provenance-generator")) throw new ProvenanceMarkError("CborError", void 0, { message: "Envelope is not a provenance-generator" });
|
|
1986
|
+
const chainId = env.subject().asByteString();
|
|
1987
|
+
if (chainId === void 0) throw new ProvenanceMarkError("CborError", void 0, { message: "Could not extract chain ID" });
|
|
1988
|
+
const extractAssertion = (predicate) => {
|
|
1989
|
+
const assertions = env.assertionsWithPredicate(predicate);
|
|
1990
|
+
if (assertions.length === 0) throw new ProvenanceMarkError("CborError", void 0, { message: `Missing ${predicate} assertion` });
|
|
1991
|
+
const assertionCase = assertions[0].case();
|
|
1992
|
+
if (assertionCase.type !== "assertion") throw new ProvenanceMarkError("CborError", void 0, { message: `Invalid ${predicate} assertion` });
|
|
1993
|
+
const obj = assertionCase.assertion.object();
|
|
1994
|
+
const objCase = obj.case();
|
|
1995
|
+
if (objCase.type === "leaf") return {
|
|
1996
|
+
cbor: objCase.cbor,
|
|
1997
|
+
bytes: obj.asByteString()
|
|
1998
|
+
};
|
|
1999
|
+
throw new ProvenanceMarkError("CborError", void 0, { message: `Invalid ${predicate} value` });
|
|
1488
2000
|
};
|
|
2001
|
+
const res = resolutionFromCbor(extractAssertion("res").cbor);
|
|
2002
|
+
const seedValue = extractAssertion("seed");
|
|
2003
|
+
if (seedValue.bytes === void 0) throw new ProvenanceMarkError("CborError", void 0, { message: "Invalid seed data" });
|
|
2004
|
+
const seed = ProvenanceSeed.fromBytes(seedValue.bytes);
|
|
2005
|
+
const seqValue = extractAssertion("next-seq");
|
|
2006
|
+
const nextSeq = Number(seqValue.cbor);
|
|
2007
|
+
const rngValue = extractAssertion("rng-state");
|
|
2008
|
+
if (rngValue.bytes === void 0) throw new ProvenanceMarkError("CborError", void 0, { message: "Invalid rng-state data" });
|
|
2009
|
+
const rngState = RngState.fromBytes(rngValue.bytes);
|
|
2010
|
+
return ProvenanceMarkGenerator.new(res, seed, chainId, nextSeq, rngState);
|
|
1489
2011
|
}
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
* Create a sequence report from flagged marks.
|
|
1494
|
-
*/
|
|
1495
|
-
function createSequenceReport(marks) {
|
|
1496
|
-
return {
|
|
1497
|
-
startSeq: marks.length > 0 ? marks[0].mark.seq() : 0,
|
|
1498
|
-
endSeq: marks.length > 0 ? marks[marks.length - 1].mark.seq() : 0,
|
|
1499
|
-
marks
|
|
1500
|
-
};
|
|
1501
|
-
}
|
|
1502
|
-
/**
|
|
1503
|
-
* Validate a collection of provenance marks.
|
|
1504
|
-
*/
|
|
1505
|
-
function validate(marks) {
|
|
1506
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1507
|
-
const deduplicatedMarks = [];
|
|
1508
|
-
for (const mark of marks) {
|
|
1509
|
-
const key = mark.toUrlEncoding();
|
|
1510
|
-
if (!seen.has(key)) {
|
|
1511
|
-
seen.add(key);
|
|
1512
|
-
deduplicatedMarks.push(mark);
|
|
1513
|
-
}
|
|
1514
|
-
}
|
|
1515
|
-
const chainBins = /* @__PURE__ */ new Map();
|
|
1516
|
-
for (const mark of deduplicatedMarks) {
|
|
1517
|
-
const chainIdKey = hexEncode(mark.chainId());
|
|
1518
|
-
const bin = chainBins.get(chainIdKey);
|
|
1519
|
-
if (bin !== void 0) bin.push(mark);
|
|
1520
|
-
else chainBins.set(chainIdKey, [mark]);
|
|
1521
|
-
}
|
|
1522
|
-
const chains = [];
|
|
1523
|
-
for (const [chainIdKey, chainMarks] of chainBins) {
|
|
1524
|
-
chainMarks.sort((a, b) => a.seq() - b.seq());
|
|
1525
|
-
const hasGenesis = chainMarks.length > 0 && chainMarks[0].seq() === 0 && chainMarks[0].isGenesis();
|
|
1526
|
-
const sequences = buildSequenceBins(chainMarks);
|
|
1527
|
-
chains.push({
|
|
1528
|
-
chainId: hexDecode(chainIdKey),
|
|
1529
|
-
hasGenesis,
|
|
1530
|
-
marks: chainMarks,
|
|
1531
|
-
sequences
|
|
1532
|
-
});
|
|
1533
|
-
}
|
|
1534
|
-
chains.sort((a, b) => hexEncode(a.chainId).localeCompare(hexEncode(b.chainId)));
|
|
1535
|
-
return {
|
|
1536
|
-
marks: deduplicatedMarks,
|
|
1537
|
-
chains
|
|
1538
|
-
};
|
|
1539
|
-
}
|
|
1540
|
-
/**
|
|
1541
|
-
* Helper function to encode bytes as hex.
|
|
1542
|
-
*/
|
|
1543
|
-
function hexEncode(bytes) {
|
|
1544
|
-
return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
1545
|
-
}
|
|
2012
|
+
};
|
|
2013
|
+
//#endregion
|
|
2014
|
+
//#region src/mark-info.ts
|
|
1546
2015
|
/**
|
|
1547
|
-
* Helper function to decode hex to bytes.
|
|
1548
|
-
*/
|
|
1549
|
-
function hexDecode(hex) {
|
|
1550
|
-
const bytes = new Uint8Array(hex.length / 2);
|
|
1551
|
-
for (let i = 0; i < hex.length; i += 2) bytes[i / 2] = parseInt(hex.slice(i, i + 2), 16);
|
|
1552
|
-
return bytes;
|
|
1553
|
-
}
|
|
1554
|
-
|
|
1555
|
-
//#endregion
|
|
1556
|
-
//#region src/mark-info.ts
|
|
1557
|
-
/**
|
|
1558
2016
|
* Wrapper for a provenance mark with additional display information.
|
|
1559
2017
|
*/
|
|
1560
2018
|
var ProvenanceMarkInfo = class ProvenanceMarkInfo {
|
|
@@ -1572,12 +2030,19 @@ var BCProvenanceMark = (function(exports, __bcts_dcbor, __noble_hashes_sha256, _
|
|
|
1572
2030
|
}
|
|
1573
2031
|
/**
|
|
1574
2032
|
* Create a new ProvenanceMarkInfo from a mark.
|
|
2033
|
+
*
|
|
2034
|
+
* Mirrors Rust `ProvenanceMarkInfo::new`
|
|
2035
|
+
* (`provenance-mark-rust/src/mark_info.rs`), which calls
|
|
2036
|
+
* `mark.ur()` — i.e. the `UREncodable` implementation, whose
|
|
2037
|
+
* payload is the **untagged** CBOR with type `"provenance"`. Earlier
|
|
2038
|
+
* revisions of this port called `decodeCbor(mark.toCborData())` and
|
|
2039
|
+
* wrapped the resulting *tagged* CBOR in `UR.new("provenance", ...)`,
|
|
2040
|
+
* which prepended the CBOR tag to the UR bytewords and broke
|
|
2041
|
+
* cross-impl interop (UR strings produced by Rust would not parse,
|
|
2042
|
+
* and vice versa).
|
|
1575
2043
|
*/
|
|
1576
2044
|
static new(mark, comment = "") {
|
|
1577
|
-
|
|
1578
|
-
if (tagName === void 0) throw new Error("PROVENANCE_MARK tag has no name");
|
|
1579
|
-
const cborValue = (0, __bcts_dcbor.decodeCbor)(mark.toCborData());
|
|
1580
|
-
return new ProvenanceMarkInfo(mark, __bcts_uniform_resources.UR.new(tagName, cborValue), mark.bytewordsIdentifier(true), mark.bytemojiIdentifier(true), comment);
|
|
2045
|
+
return new ProvenanceMarkInfo(mark, mark.ur(), mark.idBytewords(4, true), mark.idBytemoji(4, true), comment);
|
|
1581
2046
|
}
|
|
1582
2047
|
mark() {
|
|
1583
2048
|
return this._mark;
|
|
@@ -1596,12 +2061,16 @@ var BCProvenanceMark = (function(exports, __bcts_dcbor, __noble_hashes_sha256, _
|
|
|
1596
2061
|
}
|
|
1597
2062
|
/**
|
|
1598
2063
|
* Generate a markdown summary of the mark.
|
|
2064
|
+
*
|
|
2065
|
+
* Date rendering uses {@link dateToDisplay} so midnight-UTC dates
|
|
2066
|
+
* appear as `YYYY-MM-DD` (matching Rust `format!("{}",
|
|
2067
|
+
* self.mark.date())`), not as `YYYY-MM-DDT00:00:00Z`.
|
|
1599
2068
|
*/
|
|
1600
2069
|
markdownSummary() {
|
|
1601
2070
|
const lines = [];
|
|
1602
2071
|
lines.push("---");
|
|
1603
2072
|
lines.push("");
|
|
1604
|
-
lines.push(this._mark.date()
|
|
2073
|
+
lines.push(dateToDisplay(this._mark.date()));
|
|
1605
2074
|
lines.push("");
|
|
1606
2075
|
lines.push(`#### ${this._ur.toString()}`);
|
|
1607
2076
|
lines.push("");
|
|
@@ -1616,85 +2085,204 @@ var BCProvenanceMark = (function(exports, __bcts_dcbor, __noble_hashes_sha256, _
|
|
|
1616
2085
|
return lines.join("\n");
|
|
1617
2086
|
}
|
|
1618
2087
|
/**
|
|
1619
|
-
* JSON serialization.
|
|
2088
|
+
* JSON serialization. Field order mirrors Rust's `#[derive(Serialize)]`
|
|
2089
|
+
* on `ProvenanceMarkInfo` (provenance-mark-rust/src/mark_info.rs):
|
|
2090
|
+
* `ur, bytewords, bytemoji, [comment,] mark` — `comment` (when present)
|
|
2091
|
+
* comes BEFORE `mark`. Rust uses `skip_serializing_if = "String::is_empty"`,
|
|
2092
|
+
* matched here by the `if (...length > 0)` guard.
|
|
1620
2093
|
*/
|
|
1621
2094
|
toJSON() {
|
|
1622
2095
|
const result = {
|
|
1623
2096
|
ur: this._ur.toString(),
|
|
1624
2097
|
bytewords: this._bytewords,
|
|
1625
|
-
bytemoji: this._bytemoji
|
|
1626
|
-
mark: this._mark.toJSON()
|
|
2098
|
+
bytemoji: this._bytemoji
|
|
1627
2099
|
};
|
|
1628
2100
|
if (this._comment.length > 0) result["comment"] = this._comment;
|
|
2101
|
+
result["mark"] = this._mark.toJSON();
|
|
1629
2102
|
return result;
|
|
1630
2103
|
}
|
|
1631
2104
|
/**
|
|
1632
2105
|
* Create from JSON object.
|
|
2106
|
+
*
|
|
2107
|
+
* Decodes the UR string through {@link ProvenanceMark.fromURString},
|
|
2108
|
+
* which correctly handles the **untagged** CBOR payload that
|
|
2109
|
+
* `mark.ur()` produces — symmetric with the constructor.
|
|
1633
2110
|
*/
|
|
1634
2111
|
static fromJSON(json) {
|
|
1635
2112
|
const urString = json["ur"];
|
|
1636
|
-
const
|
|
1637
|
-
const
|
|
1638
|
-
const mark = ProvenanceMark.fromCborData(cborBytes);
|
|
2113
|
+
const mark = ProvenanceMark.fromURString(urString);
|
|
2114
|
+
const ur = mark.ur();
|
|
1639
2115
|
const bytewords = json["bytewords"];
|
|
1640
2116
|
const bytemoji = json["bytemoji"];
|
|
1641
2117
|
return new ProvenanceMarkInfo(mark, ur, bytewords, bytemoji, typeof json["comment"] === "string" ? json["comment"] : "");
|
|
1642
2118
|
}
|
|
1643
2119
|
};
|
|
2120
|
+
//#endregion
|
|
2121
|
+
//#region src/envelope.ts
|
|
2122
|
+
/**
|
|
2123
|
+
* Copyright © 2023-2026 Blockchain Commons, LLC
|
|
2124
|
+
* Copyright © 2025-2026 Parity Technologies
|
|
2125
|
+
*
|
|
2126
|
+
*
|
|
2127
|
+
* Envelope support for Provenance Marks
|
|
2128
|
+
*
|
|
2129
|
+
* This module provides Gordian Envelope integration for ProvenanceMark and
|
|
2130
|
+
* ProvenanceMarkGenerator, enabling them to be used with the bc-envelope
|
|
2131
|
+
* ecosystem.
|
|
2132
|
+
*
|
|
2133
|
+
* Ported from provenance-mark-rust/src/mark.rs and generator.rs (envelope feature)
|
|
2134
|
+
*/
|
|
2135
|
+
/**
|
|
2136
|
+
* Registers provenance mark tags in the global format context.
|
|
2137
|
+
*
|
|
2138
|
+
* Matches Rust: register_tags()
|
|
2139
|
+
*/
|
|
2140
|
+
function registerTags() {
|
|
2141
|
+
(0, _bcts_envelope.withFormatContextMut)((context) => {
|
|
2142
|
+
registerTagsIn(context);
|
|
2143
|
+
});
|
|
2144
|
+
}
|
|
2145
|
+
/**
|
|
2146
|
+
* Registers provenance mark tags in a specific format context.
|
|
2147
|
+
*
|
|
2148
|
+
* Matches Rust: register_tags_in()
|
|
2149
|
+
*
|
|
2150
|
+
* @param context - The format context to register tags in
|
|
2151
|
+
*/
|
|
2152
|
+
function registerTagsIn(context) {
|
|
2153
|
+
(0, _bcts_envelope.registerTagsIn)(context);
|
|
2154
|
+
context.tags().setSummarizer(BigInt(_bcts_tags.PROVENANCE_MARK.value), (untaggedCbor, _flat) => {
|
|
2155
|
+
try {
|
|
2156
|
+
return {
|
|
2157
|
+
ok: true,
|
|
2158
|
+
value: ProvenanceMark.fromUntaggedCbor(untaggedCbor).toString()
|
|
2159
|
+
};
|
|
2160
|
+
} catch {
|
|
2161
|
+
return {
|
|
2162
|
+
ok: false,
|
|
2163
|
+
error: {
|
|
2164
|
+
type: "Custom",
|
|
2165
|
+
message: "invalid provenance mark"
|
|
2166
|
+
}
|
|
2167
|
+
};
|
|
2168
|
+
}
|
|
2169
|
+
});
|
|
2170
|
+
}
|
|
2171
|
+
/**
|
|
2172
|
+
* Convert a ProvenanceMark to an Envelope.
|
|
2173
|
+
*
|
|
2174
|
+
* Delegates to ProvenanceMark.intoEnvelope() — single source of truth.
|
|
2175
|
+
*
|
|
2176
|
+
* @param mark - The provenance mark to convert
|
|
2177
|
+
* @returns An envelope containing the mark
|
|
2178
|
+
*/
|
|
2179
|
+
function provenanceMarkToEnvelope(mark) {
|
|
2180
|
+
return mark.intoEnvelope();
|
|
2181
|
+
}
|
|
2182
|
+
/**
|
|
2183
|
+
* Extract a ProvenanceMark from an Envelope.
|
|
2184
|
+
*
|
|
2185
|
+
* Delegates to ProvenanceMark.fromEnvelope() — single source of truth.
|
|
2186
|
+
*
|
|
2187
|
+
* @param envelope - The envelope to extract from
|
|
2188
|
+
* @returns The extracted provenance mark
|
|
2189
|
+
* @throws ProvenanceMarkError if extraction fails
|
|
2190
|
+
*/
|
|
2191
|
+
function provenanceMarkFromEnvelope(envelope) {
|
|
2192
|
+
return ProvenanceMark.fromEnvelope(envelope);
|
|
2193
|
+
}
|
|
2194
|
+
/**
|
|
2195
|
+
* Convert a ProvenanceMarkGenerator to an Envelope.
|
|
2196
|
+
*
|
|
2197
|
+
* Delegates to ProvenanceMarkGenerator.intoEnvelope() — single source of truth.
|
|
2198
|
+
*
|
|
2199
|
+
* @param generator - The generator to convert
|
|
2200
|
+
* @returns An envelope containing the generator
|
|
2201
|
+
*/
|
|
2202
|
+
function provenanceMarkGeneratorToEnvelope(generator) {
|
|
2203
|
+
return generator.intoEnvelope();
|
|
2204
|
+
}
|
|
2205
|
+
/**
|
|
2206
|
+
* Extract a ProvenanceMarkGenerator from an Envelope.
|
|
2207
|
+
*
|
|
2208
|
+
* Delegates to ProvenanceMarkGenerator.fromEnvelope() — single source of truth.
|
|
2209
|
+
*
|
|
2210
|
+
* @param envelope - The envelope to extract from
|
|
2211
|
+
* @returns The extracted generator
|
|
2212
|
+
* @throws ProvenanceMarkError if extraction fails
|
|
2213
|
+
*/
|
|
2214
|
+
function provenanceMarkGeneratorFromEnvelope(envelope) {
|
|
2215
|
+
return ProvenanceMarkGenerator.fromEnvelope(envelope);
|
|
2216
|
+
}
|
|
2217
|
+
//#endregion
|
|
2218
|
+
Object.defineProperty(exports, "FormatContext", {
|
|
2219
|
+
enumerable: true,
|
|
2220
|
+
get: function() {
|
|
2221
|
+
return _bcts_envelope.FormatContext;
|
|
2222
|
+
}
|
|
2223
|
+
});
|
|
2224
|
+
exports.PROVENANCE_SEED_LENGTH = PROVENANCE_SEED_LENGTH;
|
|
2225
|
+
exports.ProvenanceMark = ProvenanceMark;
|
|
2226
|
+
exports.ProvenanceMarkError = ProvenanceMarkError;
|
|
2227
|
+
exports.ProvenanceMarkErrorType = ProvenanceMarkErrorType;
|
|
2228
|
+
exports.ProvenanceMarkGenerator = ProvenanceMarkGenerator;
|
|
2229
|
+
exports.ProvenanceMarkInfo = ProvenanceMarkInfo;
|
|
2230
|
+
exports.ProvenanceMarkResolution = ProvenanceMarkResolution;
|
|
2231
|
+
exports.ProvenanceSeed = ProvenanceSeed;
|
|
2232
|
+
exports.RNG_STATE_LENGTH = RNG_STATE_LENGTH;
|
|
2233
|
+
exports.RngState = RngState;
|
|
2234
|
+
exports.SHA256_SIZE = SHA256_SIZE;
|
|
2235
|
+
exports.ValidationReportFormat = ValidationReportFormat;
|
|
2236
|
+
exports.Xoshiro256StarStar = Xoshiro256StarStar;
|
|
2237
|
+
exports.chainIdHex = chainIdHex;
|
|
2238
|
+
exports.chainIdRange = chainIdRange;
|
|
2239
|
+
exports.dateBytesLength = dateBytesLength;
|
|
2240
|
+
exports.dateBytesRange = dateBytesRange;
|
|
2241
|
+
exports.dateFromIso8601 = dateFromIso8601;
|
|
2242
|
+
exports.dateToDateString = dateToDateString;
|
|
2243
|
+
exports.dateToDisplay = dateToDisplay;
|
|
2244
|
+
exports.dateToIso8601 = dateToIso8601;
|
|
2245
|
+
exports.deserialize2Bytes = deserialize2Bytes;
|
|
2246
|
+
exports.deserialize4Bytes = deserialize4Bytes;
|
|
2247
|
+
exports.deserialize6Bytes = deserialize6Bytes;
|
|
2248
|
+
exports.deserializeDate = deserializeDate;
|
|
2249
|
+
exports.deserializeSeq = deserializeSeq;
|
|
2250
|
+
exports.extendKey = extendKey;
|
|
2251
|
+
exports.fixedLength = fixedLength;
|
|
2252
|
+
exports.formatReport = formatReport;
|
|
2253
|
+
exports.formatValidationIssue = formatValidationIssue;
|
|
2254
|
+
exports.hasIssues = hasIssues;
|
|
2255
|
+
exports.hashRange = hashRange;
|
|
2256
|
+
exports.hkdfHmacSha256 = hkdfHmacSha256;
|
|
2257
|
+
exports.infoRangeStart = infoRangeStart;
|
|
2258
|
+
exports.keyRange = keyRange;
|
|
2259
|
+
exports.linkLength = linkLength;
|
|
2260
|
+
exports.obfuscate = obfuscate;
|
|
2261
|
+
exports.parseDate = parseDate;
|
|
2262
|
+
exports.parseSeed = parseSeed;
|
|
2263
|
+
exports.provenanceMarkFromEnvelope = provenanceMarkFromEnvelope;
|
|
2264
|
+
exports.provenanceMarkGeneratorFromEnvelope = provenanceMarkGeneratorFromEnvelope;
|
|
2265
|
+
exports.provenanceMarkGeneratorToEnvelope = provenanceMarkGeneratorToEnvelope;
|
|
2266
|
+
exports.provenanceMarkToEnvelope = provenanceMarkToEnvelope;
|
|
2267
|
+
exports.rangeOfDaysInMonth = rangeOfDaysInMonth;
|
|
2268
|
+
exports.registerTags = registerTags;
|
|
2269
|
+
exports.registerTagsIn = registerTagsIn;
|
|
2270
|
+
exports.resolutionFromCbor = resolutionFromCbor;
|
|
2271
|
+
exports.resolutionFromNumber = resolutionFromNumber;
|
|
2272
|
+
exports.resolutionToCbor = resolutionToCbor;
|
|
2273
|
+
exports.resolutionToNumber = resolutionToNumber;
|
|
2274
|
+
exports.resolutionToString = resolutionToString;
|
|
2275
|
+
exports.seqBytesLength = seqBytesLength;
|
|
2276
|
+
exports.seqBytesRange = seqBytesRange;
|
|
2277
|
+
exports.serialize2Bytes = serialize2Bytes;
|
|
2278
|
+
exports.serialize4Bytes = serialize4Bytes;
|
|
2279
|
+
exports.serialize6Bytes = serialize6Bytes;
|
|
2280
|
+
exports.serializeDate = serializeDate;
|
|
2281
|
+
exports.serializeSeq = serializeSeq;
|
|
2282
|
+
exports.sha256 = sha256;
|
|
2283
|
+
exports.sha256Prefix = sha256Prefix;
|
|
2284
|
+
exports.validate = validate;
|
|
2285
|
+
return exports;
|
|
2286
|
+
})({}, bctsDcbor, bctsRand, nobleHashesSha2, nobleHashesHkdf, nobleCiphersChacha, bctsTags, bctsUniformResources, bctsEnvelope);
|
|
1644
2287
|
|
|
1645
|
-
//#endregion
|
|
1646
|
-
exports.PROVENANCE_SEED_LENGTH = PROVENANCE_SEED_LENGTH;
|
|
1647
|
-
exports.ProvenanceMark = ProvenanceMark;
|
|
1648
|
-
exports.ProvenanceMarkError = ProvenanceMarkError;
|
|
1649
|
-
exports.ProvenanceMarkErrorType = ProvenanceMarkErrorType;
|
|
1650
|
-
exports.ProvenanceMarkGenerator = ProvenanceMarkGenerator;
|
|
1651
|
-
exports.ProvenanceMarkInfo = ProvenanceMarkInfo;
|
|
1652
|
-
exports.ProvenanceMarkResolution = ProvenanceMarkResolution;
|
|
1653
|
-
exports.ProvenanceSeed = ProvenanceSeed;
|
|
1654
|
-
exports.RNG_STATE_LENGTH = RNG_STATE_LENGTH;
|
|
1655
|
-
exports.RngState = RngState;
|
|
1656
|
-
exports.SHA256_SIZE = SHA256_SIZE;
|
|
1657
|
-
exports.ValidationReportFormat = ValidationReportFormat;
|
|
1658
|
-
exports.Xoshiro256StarStar = Xoshiro256StarStar;
|
|
1659
|
-
exports.chainIdHex = chainIdHex;
|
|
1660
|
-
exports.chainIdRange = chainIdRange;
|
|
1661
|
-
exports.dateBytesLength = dateBytesLength;
|
|
1662
|
-
exports.dateBytesRange = dateBytesRange;
|
|
1663
|
-
exports.dateFromIso8601 = dateFromIso8601;
|
|
1664
|
-
exports.dateToDateString = dateToDateString;
|
|
1665
|
-
exports.dateToIso8601 = dateToIso8601;
|
|
1666
|
-
exports.deserialize2Bytes = deserialize2Bytes;
|
|
1667
|
-
exports.deserialize4Bytes = deserialize4Bytes;
|
|
1668
|
-
exports.deserialize6Bytes = deserialize6Bytes;
|
|
1669
|
-
exports.deserializeDate = deserializeDate;
|
|
1670
|
-
exports.deserializeSeq = deserializeSeq;
|
|
1671
|
-
exports.extendKey = extendKey;
|
|
1672
|
-
exports.fixedLength = fixedLength;
|
|
1673
|
-
exports.formatReport = formatReport;
|
|
1674
|
-
exports.formatValidationIssue = formatValidationIssue;
|
|
1675
|
-
exports.hasIssues = hasIssues;
|
|
1676
|
-
exports.hashRange = hashRange;
|
|
1677
|
-
exports.hkdfHmacSha256 = hkdfHmacSha256;
|
|
1678
|
-
exports.infoRangeStart = infoRangeStart;
|
|
1679
|
-
exports.keyRange = keyRange;
|
|
1680
|
-
exports.linkLength = linkLength;
|
|
1681
|
-
exports.obfuscate = obfuscate;
|
|
1682
|
-
exports.rangeOfDaysInMonth = rangeOfDaysInMonth;
|
|
1683
|
-
exports.resolutionFromCbor = resolutionFromCbor;
|
|
1684
|
-
exports.resolutionFromNumber = resolutionFromNumber;
|
|
1685
|
-
exports.resolutionToCbor = resolutionToCbor;
|
|
1686
|
-
exports.resolutionToNumber = resolutionToNumber;
|
|
1687
|
-
exports.resolutionToString = resolutionToString;
|
|
1688
|
-
exports.seqBytesLength = seqBytesLength;
|
|
1689
|
-
exports.seqBytesRange = seqBytesRange;
|
|
1690
|
-
exports.serialize2Bytes = serialize2Bytes;
|
|
1691
|
-
exports.serialize4Bytes = serialize4Bytes;
|
|
1692
|
-
exports.serialize6Bytes = serialize6Bytes;
|
|
1693
|
-
exports.serializeDate = serializeDate;
|
|
1694
|
-
exports.serializeSeq = serializeSeq;
|
|
1695
|
-
exports.sha256 = sha256;
|
|
1696
|
-
exports.sha256Prefix = sha256Prefix;
|
|
1697
|
-
exports.validate = validate;
|
|
1698
|
-
return exports;
|
|
1699
|
-
})({}, BCDcbor, __noble_hashes_sha256, __noble_hashes_hkdf, __noble_ciphers_chacha, BCRand, BCTags, BCUR);
|
|
1700
2288
|
//# sourceMappingURL=index.iife.js.map
|