@open-charging-cloud/wwcp-poi 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { encode, decode, cbor } from '@vanaheimr/metrological-cbor';
2
2
  import { sha512, sha384, sha256 } from '@noble/hashes/sha2.js';
3
- import { algorithmByName, verifyWith, CoseKey, KEY_TYPE_AKP, KEY_TYPE_OKP, KEY_TYPE_EC2 } from '@vanaheimr/cose';
3
+ import { algorithmByName, verifyWith, CoseKey, KEY_TYPE_AKP, KEY_TYPE_OKP, KEY_TYPE_EC2, DerReader, contentsOf, Tag, derObjectIdentifier, derBitString, isOnCurve, CoseCurves } from '@vanaheimr/cose';
4
4
 
5
5
  // src/errors.ts
6
6
  var POIError = class extends Error {
@@ -170,7 +170,7 @@ function sameValue(left, right) {
170
170
  return false;
171
171
  const leftKeys = Object.keys(left);
172
172
  const rightKeys = Object.keys(right);
173
- return leftKeys.length === rightKeys.length && leftKeys.every((key) => Object.prototype.hasOwnProperty.call(right, key) && sameValue(left[key], right[key]));
173
+ return leftKeys.length === rightKeys.length && leftKeys.every((key2) => Object.prototype.hasOwnProperty.call(right, key2) && sameValue(left[key2], right[key2]));
174
174
  }
175
175
  return left === right;
176
176
  }
@@ -520,12 +520,12 @@ function cborToJson(value) {
520
520
  return value.items.map(cborToJson);
521
521
  case "map": {
522
522
  const object = {};
523
- for (const [key, item] of value.entries) {
524
- if (key.type !== "text")
523
+ for (const [key2, item] of value.entries) {
524
+ if (key2.type !== "text")
525
525
  throw new POIError("A CBOR map with keys other than texts has no JSON representation!");
526
- if (Object.prototype.hasOwnProperty.call(object, key.value))
527
- throw new POIError(`A CBOR map names '${key.value}' twice!`);
528
- set(object, key.value, cborToJson(item));
526
+ if (Object.prototype.hasOwnProperty.call(object, key2.value))
527
+ throw new POIError(`A CBOR map names '${key2.value}' twice!`);
528
+ set(object, key2.value, cborToJson(item));
529
529
  }
530
530
  return object;
531
531
  }
@@ -817,22 +817,22 @@ function signatureAlgorithm(name) {
817
817
  throw new POIError(`Unknown or noncanonical signature algorithm '${name}'!`);
818
818
  return algorithm;
819
819
  }
820
- function publicKeyBytes(key, algorithm) {
820
+ function publicKeyBytes(key2, algorithm) {
821
821
  switch (algorithm.family) {
822
822
  case "ecdsa":
823
- if (key.keyType !== KEY_TYPE_EC2 || key.x === null || key.y === null)
823
+ if (key2.keyType !== KEY_TYPE_EC2 || key2.x === null || key2.y === null)
824
824
  throw new POIError(`The algorithm '${algorithm.name}' needs an elliptic curve key with x and y!`);
825
- return Uint8Array.of(4, ...key.x, ...key.y);
825
+ return Uint8Array.of(4, ...key2.x, ...key2.y);
826
826
  case "eddsa":
827
- if (key.keyType !== KEY_TYPE_OKP || key.x === null)
827
+ if (key2.keyType !== KEY_TYPE_OKP || key2.x === null)
828
828
  throw new POIError(`The algorithm '${algorithm.name}' needs an octet key pair!`);
829
- return key.x;
829
+ return key2.x;
830
830
  case "mldsa":
831
- if (key.keyType !== KEY_TYPE_AKP || key.pub === null)
831
+ if (key2.keyType !== KEY_TYPE_AKP || key2.pub === null)
832
832
  throw new POIError(`The algorithm '${algorithm.name}' needs an algorithm key pair!`);
833
- if (key.algorithm?.name !== algorithm.name)
834
- throw new POIError(`The algorithm '${algorithm.name}' differs from the one of its key, '${key.algorithm?.name ?? "none"}'!`);
835
- return key.pub;
833
+ if (key2.algorithm?.name !== algorithm.name)
834
+ throw new POIError(`The algorithm '${algorithm.name}' differs from the one of its key, '${key2.algorithm?.name ?? "none"}'!`);
835
+ return key2.pub;
836
836
  default:
837
837
  throw new POIError(`'${algorithm.name}' is no signature algorithm!`);
838
838
  }
@@ -852,19 +852,21 @@ function verifySignature(algorithmName, publicKey, input, value) {
852
852
  if (signature.length === 0)
853
853
  throw new POIError("The signature value must be nonempty canonical standard Base64!");
854
854
  const keyBytes = publicKeyBytes(publicKey, algorithm);
855
- let valid;
855
+ let valid2;
856
856
  try {
857
- valid = verifyWith(algorithm, publicKey.curve, input, signature, keyBytes);
857
+ valid2 = verifyWith(algorithm, publicKey.curve, input, signature, keyBytes);
858
858
  } catch (e) {
859
859
  throw new POIError(`The signature is invalid: ${e instanceof Error ? e.message : String(e)}`);
860
860
  }
861
- if (!valid)
861
+ if (!valid2)
862
862
  throw new POIError("The signature is invalid!");
863
863
  }
864
864
  var KeyUsages = {
865
865
  Infrastructure: "infrastructure",
866
+ ChargeTransparencyRecords: "chargeTransparencyRecords",
866
867
  Tariffs: "tariffs",
867
868
  PriceStatements: "priceStatements",
869
+ WitnessPublications: "witnessPublications",
868
870
  Models: "models",
869
871
  Software: "software",
870
872
  Certificates: "certificates",
@@ -886,9 +888,9 @@ var isDeterministicCbor = (bytes) => {
886
888
  }
887
889
  };
888
890
  var POIPublicKey = class _POIPublicKey {
889
- constructor(id, key, keyBytes, notBefore, notAfter, holder, usages, json) {
891
+ constructor(id, key2, keyBytes, notBefore, notAfter, holder, usages, json) {
890
892
  this.id = id;
891
- this.key = key;
893
+ this.key = key2;
892
894
  this.keyBytes = keyBytes;
893
895
  this.notBefore = notBefore;
894
896
  this.notAfter = notAfter;
@@ -932,20 +934,20 @@ var POIPublicKey = class _POIPublicKey {
932
934
  if (id === void 0 || keyText === void 0)
933
935
  throw new POIError("A public key needs its identification and its key!");
934
936
  let keyBytes;
935
- let key;
937
+ let key2;
936
938
  try {
937
939
  keyBytes = fromBase64(keyText);
938
940
  } catch {
939
941
  throw new POIError(isDotNetBase64(keyText) ? "A public key must be given as canonical Base64!" : `Invalid public key: ${DOTNET_BAD_BASE64}`);
940
942
  }
941
943
  try {
942
- key = CoseKey.parse(keyBytes);
944
+ key2 = CoseKey.parse(keyBytes);
943
945
  } catch (e) {
944
946
  throw new POIError(`Invalid COSE key: ${e instanceof Error ? e.message : String(e)}`);
945
947
  }
946
948
  if (!isDeterministicCbor(keyBytes))
947
949
  throw new POIError(`Invalid COSE key: Invalid deterministic CBOR: the key of the public key '${id}' is not written as the deterministic CBOR of its COSE key!`);
948
- if (key.isPrivate)
950
+ if (key2.isPrivate)
949
951
  throw new POIError("A public key must not contain private parts!");
950
952
  const notBefore = optionalTimestamp(json, "notBefore", `The notBefore of the key '${id}'`);
951
953
  const notAfter = optionalTimestamp(json, "notAfter", `The notAfter of the key '${id}'`);
@@ -973,13 +975,13 @@ var POIPublicKey = class _POIPublicKey {
973
975
  throw new POIError("Invalid public key: A public key must not expire before it becomes valid!");
974
976
  let thumbprint;
975
977
  try {
976
- thumbprint = toHex(key.thumbprint("sha256"));
978
+ thumbprint = toHex(key2.thumbprint("sha256"));
977
979
  } catch (e) {
978
980
  throw new POIError(`Invalid public key: ${e instanceof Error ? e.message : String(e)}`);
979
981
  }
980
982
  if (thumbprint !== id)
981
983
  throw new POIError(`The identification '${id}' of a public key is not its thumbprint '${thumbprint}'!`);
982
- return new _POIPublicKey(id, key, keyBytes, notBefore, notAfter, holder, usages, json);
984
+ return new _POIPublicKey(id, key2, keyBytes, notBefore, notAfter, holder, usages, json);
983
985
  }
984
986
  /** Reads an array of keys, refusing a key given twice. */
985
987
  static parseAll(value, what) {
@@ -988,7 +990,7 @@ var POIPublicKey = class _POIPublicKey {
988
990
  if (!isArray(value))
989
991
  throw new POIError(`The ${what} must be a JSON array!`);
990
992
  const keys = value.map((item) => _POIPublicKey.parse(item, `A key of the ${what}`));
991
- if (new Set(keys.map((key) => key.id)).size !== keys.length)
993
+ if (new Set(keys.map((key2) => key2.id)).size !== keys.length)
992
994
  throw new POIError(`A key of the ${what} must not be given twice!`);
993
995
  return keys;
994
996
  }
@@ -1004,16 +1006,16 @@ function parseETag(value, hexOnly = false) {
1004
1006
  throw new POIError(`Unknown ETag format '${format}'!`);
1005
1007
  if (!isHashAlgorithm(algorithm))
1006
1008
  throw new POIError(`Unknown ETag hash algorithm '${algorithm}'!`);
1007
- let digest;
1009
+ let digest2;
1008
1010
  if (encoding === "hex")
1009
- digest = fromHex(text, true);
1011
+ digest2 = fromHex(text, true);
1010
1012
  else if (encoding === "base64" && !hexOnly)
1011
- digest = fromBase64(text);
1013
+ digest2 = fromBase64(text);
1012
1014
  else
1013
1015
  throw new POIError(hexOnly ? `An ETag of a change set, commit or checkpoint must be written in hex, not in '${encoding}'!` : `Unknown ETag encoding '${encoding}'!`);
1014
- if (digest.length !== DIGEST_LENGTH[algorithm])
1015
- throw new POIError(`A ${algorithm} ETag has ${DIGEST_LENGTH[algorithm]} bytes, not ${digest.length}!`);
1016
- return { format, algorithm, digest };
1016
+ if (digest2.length !== DIGEST_LENGTH[algorithm])
1017
+ throw new POIError(`A ${algorithm} ETag has ${DIGEST_LENGTH[algorithm]} bytes, not ${digest2.length}!`);
1018
+ return { format, algorithm, digest: digest2 };
1017
1019
  }
1018
1020
  function parseETags(value, hexOnly = false, what = "The ETags") {
1019
1021
  if (!isArray(value))
@@ -1141,18 +1143,18 @@ var groupId = (letters) => {
1141
1143
  return match === null ? ignoringCase(text) : `${ignoringCase(match[1] ?? "")}*${ignoringCase(match[2] ?? "")}*${letters}${ignoringCase(match[3] ?? "")}`;
1142
1144
  };
1143
1145
  };
1144
- function twice(entities, key) {
1146
+ function twice(entities, key2) {
1145
1147
  const seen = /* @__PURE__ */ new Set();
1146
1148
  for (const entity of entities) {
1147
- const id = key(idOf(entity));
1149
+ const id = key2(idOf(entity));
1148
1150
  if (seen.has(id))
1149
1151
  return entity;
1150
1152
  seen.add(id);
1151
1153
  }
1152
1154
  return void 0;
1153
1155
  }
1154
- var givenTwice = (what, entities, key) => {
1155
- const duplicate = twice(entities, key);
1156
+ var givenTwice = (what, entities, key2) => {
1157
+ const duplicate = twice(entities, key2);
1156
1158
  return duplicate === void 0 ? void 0 : `The ${what} '${idOf(duplicate)}' must not be given twice in the roaming network!`;
1157
1159
  };
1158
1160
  var childrenOf = (parties, property) => parties.flatMap((party) => objectsOf(get(party, property)));
@@ -1440,20 +1442,20 @@ function applyTemplates(member, templates, rules) {
1440
1442
  for (const [property, rule] of Object.entries(rules.unions)) {
1441
1443
  const own = get(member, property);
1442
1444
  const elements = isArray(own) ? [...own] : [];
1443
- const key = rule.key ?? byJSON;
1444
- const taken = new Set(elements.map(key));
1445
+ const key2 = rule.key ?? byJSON;
1446
+ const taken = new Set(elements.map(key2));
1445
1447
  for (const template of templates) {
1446
1448
  const values = get(template, property);
1447
1449
  if (isArray(values)) {
1448
1450
  for (const element of values)
1449
- if (!taken.has(key(element))) {
1450
- taken.add(key(element));
1451
+ if (!taken.has(key2(element))) {
1452
+ taken.add(key2(element));
1451
1453
  elements.push(clone(element));
1452
1454
  }
1453
1455
  }
1454
1456
  }
1455
1457
  if (rule.order === "ordinal") {
1456
- const sortBy = rule.sortBy ?? key;
1458
+ const sortBy = rule.sortBy ?? key2;
1457
1459
  elements.sort((left, right) => ordinal(sortBy(left), sortBy(right)));
1458
1460
  }
1459
1461
  if (elements.length > 0)
@@ -1713,8 +1715,8 @@ function instantOf(text) {
1713
1715
  const date = /* @__PURE__ */ new Date(0);
1714
1716
  date.setUTCFullYear(Number(year), Number(month) - 1, Number(day));
1715
1717
  date.setUTCHours(Number(hour), Number(minute), Number(second), Number(digits.slice(0, 3)));
1716
- const valid = Number(year) >= 1 && date.getUTCFullYear() === Number(year) && date.getUTCMonth() === Number(month) - 1 && date.getUTCDate() === Number(day) && date.getUTCHours() === Number(hour) && date.getUTCMinutes() === Number(minute) && date.getUTCSeconds() === Number(second) && Number(offsetMinutes) <= 59 && offset <= 14 * 60;
1717
- return valid ? [date.getTime() - (sign === "-" ? -offset : offset) * 6e4, Number(digits.slice(3))] : void 0;
1718
+ const valid2 = Number(year) >= 1 && date.getUTCFullYear() === Number(year) && date.getUTCMonth() === Number(month) - 1 && date.getUTCDate() === Number(day) && date.getUTCHours() === Number(hour) && date.getUTCMinutes() === Number(minute) && date.getUTCSeconds() === Number(second) && Number(offsetMinutes) <= 59 && offset <= 14 * 60;
1719
+ return valid2 ? [date.getTime() - (sign === "-" ? -offset : offset) * 6e4, Number(digits.slice(3))] : void 0;
1718
1720
  }
1719
1721
  var invalidInstant = (json, property) => {
1720
1722
  const value = get(json, property);
@@ -1832,9 +1834,9 @@ function invalidConstruction(certificate, id) {
1832
1834
  return `The certificate '${id}' of an energy meter model names no energy meters! (Parameter 'EnergyMeters')`;
1833
1835
  const models = /* @__PURE__ */ new Map();
1834
1836
  for (const meter of energyMeters) {
1835
- const key = `${idKey(idAt(meter, "manufacturerId"))}\0${idKey(idAt(meter, "modelId"))}`;
1836
- const model = models.get(key);
1837
- models.set(key, { first: model?.first ?? meter, count: (model?.count ?? 0) + 1 });
1837
+ const key2 = `${idKey(idAt(meter, "manufacturerId"))}\0${idKey(idAt(meter, "modelId"))}`;
1838
+ const model = models.get(key2);
1839
+ models.set(key2, { first: model?.first ?? meter, count: (model?.count ?? 0) + 1 });
1838
1840
  }
1839
1841
  const twiceNamed = [...models.values()].find((model) => model.count > 1)?.first;
1840
1842
  return twiceNamed === void 0 ? void 0 : `The energy meter model '${idAt(twiceNamed, "modelId")}' of '${idAt(twiceNamed, "manufacturerId")}' must not be given twice in the certificate '${id}'! (Parameter 'EnergyMeters')`;
@@ -2104,12 +2106,12 @@ function optionalI18N(json, property, description) {
2104
2106
  }
2105
2107
  function i18nOf(value, prefix) {
2106
2108
  const texts2 = [];
2107
- for (const [key, text] of Object.entries(value)) {
2108
- const language = key.toLowerCase();
2109
+ for (const [key2, text] of Object.entries(value)) {
2110
+ const language = key2.toLowerCase();
2109
2111
  if (!LANGUAGES.has(language))
2110
- throw new POIError(`${prefix}'${key}' is no language!`);
2112
+ throw new POIError(`${prefix}'${key2}' is no language!`);
2111
2113
  if (typeof text !== "string")
2112
- throw new POIError(`${prefix}the text in '${key}' must be a text!`);
2114
+ throw new POIError(`${prefix}the text in '${key2}' must be a text!`);
2113
2115
  if (texts2.some(([other]) => other === language))
2114
2116
  throw new POIError(`${prefix}the language '${language}' is given twice!`);
2115
2117
  texts2.push([language, text]);
@@ -2134,8 +2136,8 @@ var ignoringCase2 = (text) => Array.from(text, (character) => {
2134
2136
  }).join("");
2135
2137
  var sameId = (left, right) => left !== void 0 && right !== void 0 && ignoringCase2(left) === ignoringCase2(right);
2136
2138
  var sameText = (left, right) => left === void 0 ? right === void 0 : sameId(left, right);
2137
- function distinct(values, key, description) {
2138
- if (new Set(values.map(key)).size !== values.length)
2139
+ function distinct(values, key2, description) {
2140
+ if (new Set(values.map(key2)).size !== values.length)
2139
2141
  throw new POIError(`An ${description} must not be given twice!`);
2140
2142
  return [...values];
2141
2143
  }
@@ -2215,8 +2217,8 @@ function urlOf(json, property, description) {
2215
2217
  }
2216
2218
  function lenientI18N(value) {
2217
2219
  const texts2 = /* @__PURE__ */ new Map();
2218
- for (const [key, text] of Object.entries(value)) {
2219
- const language = trimmed(key).toLowerCase();
2220
+ for (const [key2, text] of Object.entries(value)) {
2221
+ const language = trimmed(key2).toLowerCase();
2220
2222
  if (typeof text === "string" && (LANGUAGES.has(language) || /^[+-]?\d+$/u.test(language)))
2221
2223
  texts2.set(language, text);
2222
2224
  }
@@ -2389,9 +2391,9 @@ function keysOf(party, property, description) {
2389
2391
  if (!isObject(item))
2390
2392
  throw new POIError(`Invalid ${description} '${compactJSON(item)}'!`);
2391
2393
  try {
2392
- const key = POIPublicKey.parse(item);
2393
- onlyProperties(item, KEY_PROPERTIES, `The public key '${key.id}'`);
2394
- return key;
2394
+ const key2 = POIPublicKey.parse(item);
2395
+ onlyProperties(item, KEY_PROPERTIES, `The public key '${key2.id}'`);
2396
+ return key2;
2395
2397
  } catch (e) {
2396
2398
  throw new POIError(`Invalid ${description} '${compactJSON(item)}': ${e instanceof Error ? e.message : String(e)}`);
2397
2399
  }
@@ -2402,16 +2404,16 @@ function partyKeysOf(party, name, kind) {
2402
2404
  const identityKeys = keysOf(party, "identityKeys", "identity key");
2403
2405
  const invalid = (message) => new POIError(`The given JSON representation of ${kind} is invalid: ${message}`);
2404
2406
  for (const [keys, description] of [[signingKeys, "signing key"], [identityKeys, "identity key"]]) {
2405
- const ids = keys.map((key) => key.id);
2407
+ const ids = keys.map((key2) => key2.id);
2406
2408
  if (new Set(ids).size !== ids.length)
2407
2409
  throw invalid(`An ${description} of ${name} must not be given twice!`);
2408
2410
  }
2409
- const signingIds = signingKeys.map((key) => key.id).sort(ordinal);
2411
+ const signingIds = signingKeys.map((key2) => key2.id).sort(ordinal);
2410
2412
  const identities = [...identityKeys].sort((a, b) => ordinal(a.id, b.id));
2411
- const both = signingIds.find((id) => identityKeys.some((key) => key.id === id));
2413
+ const both = signingIds.find((id) => identityKeys.some((key2) => key2.id === id));
2412
2414
  if (both !== void 0)
2413
2415
  throw invalid(`The key '${both}' of ${name} must not be both a signing and an identity key!`);
2414
- const withUsages = identities.find((key) => key.usages.length > 0);
2416
+ const withUsages = identities.find((key2) => key2.usages.length > 0);
2415
2417
  if (withUsages !== void 0)
2416
2418
  throw invalid(`The identity key '${withUsages.id}' of ${name} names no usages: they are of signing keys!`);
2417
2419
  return { signingKeys, identityKeys };
@@ -3449,23 +3451,23 @@ function asRead(value, kind) {
3449
3451
  if (!isObject(value))
3450
3452
  return value;
3451
3453
  const known = PROPERTIES_OF[kind];
3452
- return Object.fromEntries(Object.entries(value).filter(([key]) => known === void 0 || known.includes(key)).map(([key, element]) => [key, PROPERTIES_OF[key] === void 0 ? element : asRead(element, key)]));
3454
+ return Object.fromEntries(Object.entries(value).filter(([key2]) => known === void 0 || known.includes(key2)).map(([key2, element]) => [key2, PROPERTIES_OF[key2] === void 0 ? element : asRead(element, key2)]));
3453
3455
  }
3454
3456
  function notWritten(value, property, path, of) {
3455
3457
  const eachOnce = of === "new value";
3456
- const join = (at, key) => at === "" ? key : `${at}.${key}`;
3458
+ const join = (at, key2) => at === "" ? key2 : `${at}.${key2}`;
3457
3459
  const holdsAs = (at, written) => ({ path: at, reason: `the content holds it as '${contentText(written)}'` });
3458
- const leftOut = (key, element, kind, keepsEmptyName) => {
3460
+ const leftOut = (key2, element, kind, keepsEmptyName) => {
3459
3461
  if (element === null)
3460
3462
  return "a property without a value is left out";
3461
- if (isArray(element) && element.length === 0 && LEFT_OUT_WHEN_EMPTY.has(key) && !(key === "identityKeys" && kind === "keyStatement"))
3463
+ if (isArray(element) && element.length === 0 && LEFT_OUT_WHEN_EMPTY.has(key2) && !(key2 === "identityKeys" && kind === "keyStatement"))
3462
3464
  return "a list without elements is left out";
3463
- if (TEXTS_IN_LANGUAGES.has(key) && isObject(element) && languagesOf(element).length === 0 && !(key === "name" && keepsEmptyName))
3465
+ if (TEXTS_IN_LANGUAGES.has(key2) && isObject(element) && languagesOf(element).length === 0 && !(key2 === "name" && keepsEmptyName))
3464
3466
  return "the content leaves it out";
3465
- if (key === "dataLicenseIds")
3467
+ if (key2 === "dataLicenseIds")
3466
3468
  return "the content leaves it out";
3467
3469
  const known = kind === void 0 ? void 0 : PROPERTIES_OF[kind];
3468
- if (known !== void 0 && !known.includes(key))
3470
+ if (known !== void 0 && !known.includes(key2))
3469
3471
  return "the content leaves it out";
3470
3472
  return void 0;
3471
3473
  };
@@ -3476,7 +3478,7 @@ function notWritten(value, property, path, of) {
3476
3478
  if (name !== void 0 && ORDERED_TEXTS.has(name) && item.some((element) => typeof element === "string" && trimmed(element) !== element))
3477
3479
  return { path: at, reason: "the texts of its elements are trimmed" };
3478
3480
  const keys = orderKeys(item, name);
3479
- if (keys?.some((key, index) => index > 0 && ordinal(keys[index - 1] ?? "", key) >= (eachOnce ? 0 : 1)) === true)
3481
+ if (keys?.some((key2, index) => index > 0 && ordinal(keys[index - 1] ?? "", key2) >= (eachOnce ? 0 : 1)) === true)
3480
3482
  return { path: at, reason: eachOnce ? "its elements are in the order the content holds them in, each once" : "its elements are in the order the content holds them in" };
3481
3483
  for (const [index, element] of item.entries()) {
3482
3484
  const elementAt = `${at}[${String(index)}]`;
@@ -3497,22 +3499,22 @@ function notWritten(value, property, path, of) {
3497
3499
  const keepsEmptyName = name === void 0 && holder === "chargingStationOperators";
3498
3500
  if (name !== void 0 && TEXTS_IN_LANGUAGES.has(name) && [...Object.keys(item)].sort(ordinal).join("\0") !== languagesOf(item).sort(ordinal).join("\0"))
3499
3501
  return { path: at, reason: "a text in languages names each language as Styx writes it, in lower case, with a text" };
3500
- for (const [key, element] of Object.entries(item)) {
3501
- if (key === "@context") {
3502
+ for (const [key2, element] of Object.entries(item)) {
3503
+ if (key2 === "@context") {
3502
3504
  if (root)
3503
3505
  continue;
3504
3506
  return { path: at, reason: "embedded, an object names no JSON-LD context" };
3505
3507
  }
3506
- const elementAt = join(at, key);
3507
- const omitted2 = leftOut(key, element, kind, keepsEmptyName);
3508
+ const elementAt = join(at, key2);
3509
+ const omitted2 = leftOut(key2, element, kind, keepsEmptyName);
3508
3510
  if (omitted2 !== void 0)
3509
3511
  return { path: elementAt, reason: omitted2 };
3510
- if (typeof element === "string" && TIMESTAMP_PROPERTIES.has(key) && !TIMESTAMP3.test(element))
3512
+ if (typeof element === "string" && TIMESTAMP_PROPERTIES.has(key2) && !TIMESTAMP3.test(element))
3511
3513
  return { path: elementAt, reason: "a timestamp is UTC to the millisecond, yyyy-MM-ddTHH:mm:ss.fffZ" };
3512
- const written = key === "customData" ? void 0 : writtenValueOf(key, element, kind);
3514
+ const written = key2 === "customData" ? void 0 : writtenValueOf(key2, element, kind);
3513
3515
  if (written !== void 0 && contentText(written) !== contentText(element))
3514
3516
  return holdsAs(elementAt, written);
3515
- const found = check2(element, key, kind, elementAt);
3517
+ const found = check2(element, key2, kind, elementAt);
3516
3518
  if (found !== void 0)
3517
3519
  return found;
3518
3520
  }
@@ -3537,10 +3539,10 @@ function notWrittenAsHeld(content) {
3537
3539
  return value.map(unknownLicense).find((found2) => found2 !== void 0);
3538
3540
  if (!isObject(value))
3539
3541
  return void 0;
3540
- for (const [key, element] of Object.entries(value)) {
3541
- if (key === "customData")
3542
+ for (const [key2, element] of Object.entries(value)) {
3543
+ if (key2 === "customData")
3542
3544
  continue;
3543
- const unknown = key === "dataLicenseIds" && isArray(element) ? element.find((id) => typeof id === "string" && !KNOWN_DATA_LICENSES.has(id)) : void 0;
3545
+ const unknown = key2 === "dataLicenseIds" && isArray(element) ? element.find((id) => typeof id === "string" && !KNOWN_DATA_LICENSES.has(id)) : void 0;
3544
3546
  if (unknown !== void 0)
3545
3547
  return `The data license '${asText(unknown)}' is not well-known, so it must be given completely!`;
3546
3548
  const found2 = unknownLicense(element);
@@ -3719,8 +3721,8 @@ function prunedDigest(entity, kind, algorithm, path = "$") {
3719
3721
  return { digest: leafHash(algorithm, canonicalJSONBytes(skeletonJSON)), attested };
3720
3722
  }
3721
3723
  function prunedTree(roamingNetwork, algorithm) {
3722
- const { digest, attested } = prunedDigest(roamingNetwork, "roamingNetwork", algorithm);
3723
- return { root: digest, attested };
3724
+ const { digest: digest2, attested } = prunedDigest(roamingNetwork, "roamingNetwork", algorithm);
3725
+ return { root: digest2, attested };
3724
3726
  }
3725
3727
 
3726
3728
  // src/proofs/keyStatement.ts
@@ -3788,9 +3790,9 @@ var KeyStatement = class _KeyStatement {
3788
3790
  return { algorithm, keyId, value: value2 };
3789
3791
  });
3790
3792
  onlyProperties(json, ["@context", "roamingNetworkId", "roamingNetworkOperatorId", "identityKeys", "minConsensus", "signedAt", "validUntil", "signatures"], "A key statement");
3791
- if (identityKeys.some((key, index) => {
3793
+ if (identityKeys.some((key2, index) => {
3792
3794
  const before = identityKeys[index - 1];
3793
- return before !== void 0 && before.id >= key.id;
3795
+ return before !== void 0 && before.id >= key2.id;
3794
3796
  }))
3795
3797
  throw new POIError("The identity keys of a key statement are written in the order of their @ids!");
3796
3798
  if (validUntil < signedAt)
@@ -3803,7 +3805,7 @@ var KeyStatement = class _KeyStatement {
3803
3805
  "@context": KEY_STATEMENT_CONTEXT,
3804
3806
  roamingNetworkId: this.roamingNetworkId,
3805
3807
  roamingNetworkOperatorId: this.roamingNetworkOperatorId,
3806
- identityKeys: this.identityKeys.map((key) => without(key.json, "@context")),
3808
+ identityKeys: this.identityKeys.map((key2) => without(key2.json, "@context")),
3807
3809
  minConsensus: this.minConsensus,
3808
3810
  signedAt: toTimestamp(this.signedAt),
3809
3811
  validUntil: toTimestamp(this.validUntil)
@@ -3826,8 +3828,8 @@ var KeyStatement = class _KeyStatement {
3826
3828
  /** The `@id`s of its identity keys per holder, as it names their holders: what a trust anchor of it pins (8.6). */
3827
3829
  get holders() {
3828
3830
  const holders = /* @__PURE__ */ new Map();
3829
- for (const key of this.identityKeys)
3830
- holders.set(key.holderOrUnnamed, [...holders.get(key.holderOrUnnamed) ?? [], key.id]);
3831
+ for (const key2 of this.identityKeys)
3832
+ holders.set(key2.holderOrUnnamed, [...holders.get(key2.holderOrUnnamed) ?? [], key2.id]);
3831
3833
  return [...holders.values()];
3832
3834
  }
3833
3835
  /**
@@ -3880,7 +3882,7 @@ var KeyStatement = class _KeyStatement {
3880
3882
  if (only2 !== void 0 && this.signatures.length === 1 && required2 === 1 && !trusted.has(only2.keyId))
3881
3883
  throw new POIError(`The key statement is signed by the key '${only2.keyId}', which is not trusted!`);
3882
3884
  for (const signature of this.signatures) {
3883
- const identityKey = keys.find((key) => key.id === signature.keyId);
3885
+ const identityKey = keys.find((key2) => key2.id === signature.keyId);
3884
3886
  if (identityKey === void 0)
3885
3887
  throw new POIError(`The identity key '${signature.keyId}' that signed the key statement is not given!`);
3886
3888
  if (!identityKey.isValidAt(this.signedAt))
@@ -3894,7 +3896,7 @@ var KeyStatement = class _KeyStatement {
3894
3896
  throw new POIError(`The key statement is signed by trusted identity keys of ${holders.size} holder${holders.size === 1 ? "" : "s"}, not of ${required2}!`);
3895
3897
  if (options.at !== void 0 && !this.isValidAt(options.at))
3896
3898
  throw new POIError(`The key statement is not valid at ${toTimestamp(options.at)}: it was signed at ${toTimestamp(this.signedAt)} and is valid until ${toTimestamp(this.validUntil)}!`);
3897
- const trustedThen = new Map(this.identityKeys.map((key) => [key.id, key.holderOrUnnamed]));
3899
+ const trustedThen = new Map(this.identityKeys.map((key2) => [key2.id, key2.holderOrUnnamed]));
3898
3900
  for (const [id, holder] of trusted)
3899
3901
  if (!trustedThen.has(id))
3900
3902
  trustedThen.set(id, nameOfHolder(holder));
@@ -3904,7 +3906,7 @@ var KeyStatement = class _KeyStatement {
3904
3906
 
3905
3907
  // src/proofs/signingKeyCertificate.ts
3906
3908
  var SIGNING_KEY_CERTIFICATE_CONTEXT = CONTEXT_PREFIX + "roamingNetwork/signingKeyCertificate/v2";
3907
- var describeHolder = (key) => key.holder !== void 0 ? `the holder '${key.holder}'` : "no named holder";
3909
+ var describeHolder = (key2) => key2.holder !== void 0 ? `the holder '${key2.holder}'` : "no named holder";
3908
3910
  var SigningKeyCertificate = class _SigningKeyCertificate {
3909
3911
  constructor(roamingNetworkOperatorId, signingKey, signedAt, algorithm, keyId, value, json) {
3910
3912
  this.roamingNetworkOperatorId = roamingNetworkOperatorId;
@@ -3965,7 +3967,7 @@ var SigningKeyCertificate = class _SigningKeyCertificate {
3965
3967
  * where trusted keys are given, of the same holder as the signing key, valid at `signedAt`. Returns that identity key.
3966
3968
  */
3967
3969
  verify(identityKeys, trustedIdentityKeyIds) {
3968
- const identityKey = identityKeys.find((key) => key.id === this.keyId);
3970
+ const identityKey = identityKeys.find((key2) => key2.id === this.keyId);
3969
3971
  if (identityKey === void 0)
3970
3972
  throw new POIError(`The identity key '${this.keyId}' that certified the signing key '${this.signingKey.id}' is not given!`);
3971
3973
  if (trustedIdentityKeyIds !== void 0 && !trustedIdentityKeyIds.has(this.keyId))
@@ -3988,7 +3990,7 @@ var ROAMING_NETWORK_CONTEXT = CONTEXT_PREFIX + "roamingNetwork/v1";
3988
3990
  var KeyStatementError = class extends POIError {
3989
3991
  };
3990
3992
  var sameId2 = (left, right) => ignoringCase(trimmed(left)) === ignoringCase(trimmed(right));
3991
- var holdersOf = (keys) => new Set(keys.map((key) => key.holderOrUnnamed));
3993
+ var holdersOf = (keys) => new Set(keys.map((key2) => key2.holderOrUnnamed));
3992
3994
  var RoamingNetworkOperator = class _RoamingNetworkOperator {
3993
3995
  constructor(id, identityKeys, minConsensus, keyStatement, signingKeyCertificates, json) {
3994
3996
  this.id = id;
@@ -4046,7 +4048,7 @@ var RoamingNetworkOperator = class _RoamingNetworkOperator {
4046
4048
  certified.set(certificate.signingKey.id, certificate.signingKey);
4047
4049
  }
4048
4050
  const signingKeys = [...certified.values()];
4049
- const both = [...signingKeys].sort((a, b) => a.id < b.id ? -1 : a.id > b.id ? 1 : 0).find((key) => identityKeys.some((identityKey) => identityKey.id === key.id));
4051
+ const both = [...signingKeys].sort((a, b) => a.id < b.id ? -1 : a.id > b.id ? 1 : 0).find((key2) => identityKeys.some((identityKey) => identityKey.id === key2.id));
4050
4052
  if (both !== void 0)
4051
4053
  throw invalid(`The key '${both.id}' of the roaming network operator '${id}' must not be both a signing and an identity key!`);
4052
4054
  for (const [kind, keys] of [["signing", signingKeys], ["identity", identityKeys]]) {
@@ -4075,15 +4077,15 @@ var RoamingNetworkOperator = class _RoamingNetworkOperator {
4075
4077
  if (keyStatement !== void 0) {
4076
4078
  if (!sameId2(keyStatement.roamingNetworkOperatorId, id))
4077
4079
  throw new KeyStatementError(`The key statement of the roaming network operator '${id}' is one of '${keyStatement.roamingNetworkOperatorId}'!`);
4078
- const unknown = keyStatement.identityKeys.find((key) => !identityKeys.some((identityKey) => sameValue(without(asRead(identityKey.json, "identityKeys"), "@context"), without(key.json, "@context"))));
4080
+ const unknown = keyStatement.identityKeys.find((key2) => !identityKeys.some((identityKey) => sameValue(without(asRead(identityKey.json, "identityKeys"), "@context"), without(key2.json, "@context"))));
4079
4081
  if (unknown !== void 0)
4080
4082
  throw new KeyStatementError(`The key statement of the roaming network operator '${id}' names the identity key '${unknown.id}', which is not one of its identity keys!`);
4081
4083
  if (keyStatement.minConsensus !== minConsensus)
4082
4084
  throw new KeyStatementError(`The key statement of the roaming network operator '${id}' states the minConsensus ${keyStatement.minConsensus}, not its ${minConsensus}!`);
4083
4085
  try {
4084
4086
  const holders = /* @__PURE__ */ new Map();
4085
- for (const key of identityKeys)
4086
- holders.set(key.holderOrUnnamed, [...holders.get(key.holderOrUnnamed) ?? [], key.id]);
4087
+ for (const key2 of identityKeys)
4088
+ holders.set(key2.holderOrUnnamed, [...holders.get(key2.holderOrUnnamed) ?? [], key2.id]);
4087
4089
  keyStatement.verifyHolders([...holders.values()], identityKeys, { minConsensus });
4088
4090
  } catch (e) {
4089
4091
  throw new KeyStatementError(minConsensus > 1 ? `The key statement of the roaming network operator '${id}' is not signed by identity keys of ${minConsensus} of its holders: ${e instanceof Error ? e.message : String(e)}` : `The key statement of the roaming network operator '${id}' is not signed by one of its identity keys: ${e instanceof Error ? e.message : String(e)}`);
@@ -4222,6 +4224,91 @@ var RoamingNetwork = class _RoamingNetwork {
4222
4224
  }
4223
4225
  };
4224
4226
 
4227
+ // src/history/epoch.ts
4228
+ var EPOCH_PROFILES_CONTEXT = CONTEXT_PREFIX + "roamingNetwork/epochProfiles/v1";
4229
+ var EPOCH_CONTEXT = CONTEXT_PREFIX + "roamingNetwork/epoch/v1";
4230
+ var EPOCH_LINK_CONTEXT = CONTEXT_PREFIX + "roamingNetwork/epochLink/v1";
4231
+ var STYX_CANONICAL_JSON = "styx-canonical-json-v1";
4232
+ var COMMIT_CONTEXT = CONTEXT_PREFIX + "roamingNetwork/commit/v3";
4233
+ var CHECKPOINT_CONTEXT = CONTEXT_PREFIX + "roamingNetwork/checkpoint/v1";
4234
+ var PRICE_STATEMENT_CONTEXT = CONTEXT_PREFIX + "roamingNetwork/priceStatement/v1";
4235
+ var EpochProfiles = class _EpochProfiles {
4236
+ constructor(identificationHash, signatureAlgorithms) {
4237
+ this.identificationHash = identificationHash;
4238
+ this.signatureAlgorithms = signatureAlgorithms;
4239
+ }
4240
+ identificationHash;
4241
+ signatureAlgorithms;
4242
+ /** The profiles of the first epoch: SHA-256 identifications, every signature algorithm. */
4243
+ static FIRST = new _EpochProfiles("sha256", []);
4244
+ /** Reads profiles as `RoamingNetworkEpochProfiles.TryParse` does, refusing those this library can not calculate. */
4245
+ static parse(value) {
4246
+ const json = requireObject(value, "The profiles of an epoch");
4247
+ onlyProperties(json, ["@context", "canonicalization", "identificationHash", "commit", "checkpoint", "priceStatement", "signatureAlgorithms"], "The profiles of an epoch");
4248
+ if (get(json, "@context") !== void 0 && get(json, "@context") !== EPOCH_PROFILES_CONTEXT)
4249
+ throw new POIError(`The JSON-LD context of the profiles of an epoch must be '${EPOCH_PROFILES_CONTEXT}'!`);
4250
+ if (get(json, "canonicalization") !== STYX_CANONICAL_JSON)
4251
+ throw new POIError(`The canonicalization of an epoch must be '${STYX_CANONICAL_JSON}', the one this library calculates!`);
4252
+ const identificationHash = get(json, "identificationHash");
4253
+ if (!isHashAlgorithm(identificationHash))
4254
+ throw new POIError("The identification hash of an epoch must be sha256, sha384 or sha512!");
4255
+ if (get(json, "commit") !== COMMIT_CONTEXT || get(json, "checkpoint") !== CHECKPOINT_CONTEXT || get(json, "priceStatement") !== PRICE_STATEMENT_CONTEXT)
4256
+ throw new POIError(`The JSON-LD contexts of commits, checkpoints and price statements of an epoch must be '${COMMIT_CONTEXT}', '${CHECKPOINT_CONTEXT}' and '${PRICE_STATEMENT_CONTEXT}', the ones this library calculates!`);
4257
+ const algorithmsJSON = get(json, "signatureAlgorithms");
4258
+ const algorithms = [];
4259
+ if (algorithmsJSON !== void 0) {
4260
+ if (!isArray(algorithmsJSON) || !algorithmsJSON.every((name) => typeof name === "string"))
4261
+ throw new POIError("The signature algorithms of an epoch must be a JSON array of COSE names!");
4262
+ for (const name of algorithmsJSON) {
4263
+ signatureAlgorithm(name);
4264
+ if (!algorithms.includes(name))
4265
+ algorithms.push(name);
4266
+ }
4267
+ }
4268
+ return new _EpochProfiles(identificationHash, algorithms.sort());
4269
+ }
4270
+ /** Whether the epoch accepts signatures of the algorithm. */
4271
+ allowsSignatureAlgorithm(algorithm) {
4272
+ return this.signatureAlgorithms.length === 0 ? SIGNATURE_ALGORITHMS.includes(algorithm) : this.signatureAlgorithms.includes(algorithm);
4273
+ }
4274
+ toJSON() {
4275
+ return {
4276
+ canonicalization: STYX_CANONICAL_JSON,
4277
+ identificationHash: this.identificationHash,
4278
+ commit: COMMIT_CONTEXT,
4279
+ checkpoint: CHECKPOINT_CONTEXT,
4280
+ priceStatement: PRICE_STATEMENT_CONTEXT,
4281
+ signatureAlgorithms: [...this.signatureAlgorithms]
4282
+ };
4283
+ }
4284
+ toString() {
4285
+ return `${this.identificationHash}, ${this.signatureAlgorithms.length === 0 ? "every signature algorithm" : this.signatureAlgorithms.join(", ")}`;
4286
+ }
4287
+ };
4288
+ var FIRST_EPOCH = { number: 1, profiles: EpochProfiles.FIRST };
4289
+ function parseEpoch(value, what = "An epoch") {
4290
+ const json = requireObject(value, what);
4291
+ onlyProperties(json, ["@context", "epoch", "profiles", "previous", "previousProfiles"], what);
4292
+ if (get(json, "@context") !== void 0 && get(json, "@context") !== EPOCH_CONTEXT)
4293
+ throw new POIError(`The JSON-LD context of the epoch must be '${EPOCH_CONTEXT}'!`);
4294
+ return {
4295
+ number: requireInteger(json, "epoch", 1, 4294967295, `The number of ${what.toLowerCase()}`),
4296
+ profiles: EpochProfiles.parse(get(json, "profiles"))
4297
+ };
4298
+ }
4299
+ var sameEpoch = (left, right) => left.number === right.number && left.profiles.identificationHash === right.profiles.identificationHash && left.profiles.signatureAlgorithms.join() === right.profiles.signatureAlgorithms.join();
4300
+ function parseEpochLink(value) {
4301
+ const json = requireObject(value, "The epoch before");
4302
+ onlyProperties(json, ["@context", "epoch", "checkpointId", "eTags"], "The epoch before");
4303
+ if (get(json, "@context") !== void 0 && get(json, "@context") !== EPOCH_LINK_CONTEXT)
4304
+ throw new POIError(`The JSON-LD context of the epoch before must be '${EPOCH_LINK_CONTEXT}'!`);
4305
+ return {
4306
+ epoch: requireInteger(json, "epoch", 1, 4294967295, "The number of the epoch before"),
4307
+ checkpointId: requireString(json, "checkpointId", "The final checkpoint of the epoch before"),
4308
+ eTags: parseETags(get(json, "eTags"), true, "The ETags of the epoch before")
4309
+ };
4310
+ }
4311
+
4225
4312
  // src/proofs/trustAnchor.ts
4226
4313
  var TRUST_ANCHOR_CONTEXT = CONTEXT_PREFIX + "roamingNetwork/trustAnchor/v2";
4227
4314
  var TRUST_ANCHOR_QR_PREFIX = "WWCP/TA-2:";
@@ -4312,7 +4399,7 @@ var TrustAnchor = class _TrustAnchor {
4312
4399
  const keys = requireArray(get(json, "keys"), "The keys of a trust anchor").map((holder) => {
4313
4400
  if (typeof holder === "string")
4314
4401
  return holder;
4315
- if (Array.isArray(holder) && holder.length > 0 && holder.every((key) => typeof key === "string"))
4402
+ if (Array.isArray(holder) && holder.length > 0 && holder.every((key2) => typeof key2 === "string"))
4316
4403
  return holder;
4317
4404
  throw new POIError("A trust anchor names the identity keys of the roaming network operator by their @ids, per holder!");
4318
4405
  });
@@ -4326,12 +4413,12 @@ var TrustAnchor = class _TrustAnchor {
4326
4413
  if (value.type !== "map")
4327
4414
  throw new POIError("The CBOR representation of a trust anchor must be a map!");
4328
4415
  const values = /* @__PURE__ */ new Map();
4329
- for (const [key, item] of value.entries) {
4330
- if (key.type !== "text")
4416
+ for (const [key2, item] of value.entries) {
4417
+ if (key2.type !== "text")
4331
4418
  throw new POIError("The CBOR representation of a trust anchor must be a map with text keys!");
4332
- if (values.has(key.value))
4333
- throw new POIError(`The CBOR representation of a trust anchor names '${key.value}' twice!`);
4334
- values.set(key.value, item);
4419
+ if (values.has(key2.value))
4420
+ throw new POIError(`The CBOR representation of a trust anchor names '${key2.value}' twice!`);
4421
+ values.set(key2.value, item);
4335
4422
  }
4336
4423
  const context = values.get("@context");
4337
4424
  if (context !== void 0 && !(context.type === "text" && context.value === TRUST_ANCHOR_CONTEXT))
@@ -4345,10 +4432,10 @@ var TrustAnchor = class _TrustAnchor {
4345
4432
  const keys = values.get("keys");
4346
4433
  if (keys?.type !== "array")
4347
4434
  throw new POIError("The identity keys of a trust anchor must be an array, one entry per holder!");
4348
- const thumbprint = (key) => {
4349
- if (key.type !== "bytes" || key.value.length !== 32)
4435
+ const thumbprint = (key2) => {
4436
+ if (key2.type !== "bytes" || key2.value.length !== 32)
4350
4437
  throw new POIError("The identity keys of a trust anchor must be their SHA-256 thumbprints, 32 bytes each, per holder!");
4351
- return toHex(key.value);
4438
+ return toHex(key2.value);
4352
4439
  };
4353
4440
  const holders = keys.items.map((holder) => holder.type === "array" ? holder.items.map(thumbprint) : thumbprint(holder));
4354
4441
  let quorum = 1;
@@ -4389,7 +4476,7 @@ var TrustAnchor = class _TrustAnchor {
4389
4476
  [cbor.text("@id"), cbor.text(this.id)],
4390
4477
  [cbor.text("@context"), cbor.text(TRUST_ANCHOR_CONTEXT)],
4391
4478
  [cbor.text("urls"), cbor.array(this.urls.map((url) => cbor.text(url)))],
4392
- [cbor.text("keys"), cbor.array(this.holders.map((keys) => keys.length === 1 ? cbor.bytes(fromHex(required(keys[0], "The key of a holder"), true)) : cbor.array(keys.map((key) => cbor.bytes(fromHex(key, true))))))]
4479
+ [cbor.text("keys"), cbor.array(this.holders.map((keys) => keys.length === 1 ? cbor.bytes(fromHex(required(keys[0], "The key of a holder"), true)) : cbor.array(keys.map((key2) => cbor.bytes(fromHex(key2, true))))))]
4393
4480
  ];
4394
4481
  if (Object.keys(this.name).length > 0)
4395
4482
  entries.push([cbor.text("name"), cbor.map(Object.entries(this.name).map(([language, text]) => [cbor.text(language), cbor.text(text)]))]);
@@ -4448,7 +4535,7 @@ function checkTrustAnchor(urls, holders, minConsensus) {
4448
4535
  throw new POIError(`The URLs of a trust anchor name one roaming network, not '${networkId}' and '${urlNetworkId}'!`);
4449
4536
  }
4450
4537
  const keys = holders.flat();
4451
- if (keys.length === 0 || holders.some((holder) => holder.length === 0) || keys.some((key) => !/^[0-9a-f]{64}$/.test(key)) || new Set(keys).size !== keys.length)
4538
+ if (keys.length === 0 || holders.some((holder) => holder.length === 0) || keys.some((key2) => !/^[0-9a-f]{64}$/.test(key2)) || new Set(keys).size !== keys.length)
4452
4539
  throw new POIError("A trust anchor names at least one identity key by its @id, 64 lowercase hexadecimal digits, each once, and every holder with at least one!");
4453
4540
  if (minConsensus < 1 || minConsensus > holders.length)
4454
4541
  throw new POIError(`The minConsensus ${minConsensus} of a trust anchor needs identity keys of as many holders, not of ${holders.length}!`);
@@ -4457,8 +4544,11 @@ function checkTrustAnchor(urls, holders, minConsensus) {
4457
4544
 
4458
4545
  // src/proofs/trustContext.ts
4459
4546
  var TrustContext = class _TrustContext {
4460
- constructor(anchor, holders, statement, evaluatedAt) {
4547
+ constructor(anchor, holders, statement, evaluatedAt, epoch = FIRST_EPOCH, epochs = [FIRST_EPOCH]) {
4548
+ this.statement = statement;
4461
4549
  this.evaluatedAt = evaluatedAt;
4550
+ this.epoch = epoch;
4551
+ this.epochs = epochs;
4462
4552
  this.anchor = frozenAnchor(anchor);
4463
4553
  this.trustedHolders = Object.freeze(holders.map((holder) => Object.freeze([...holder])));
4464
4554
  this.authorizedAnchor = frozenAnchor(TrustAnchor.create(
@@ -4476,7 +4566,10 @@ var TrustContext = class _TrustContext {
4476
4566
  this.validUntil = statement.validUntil;
4477
4567
  Object.freeze(this);
4478
4568
  }
4569
+ statement;
4479
4570
  evaluatedAt;
4571
+ epoch;
4572
+ epochs;
4480
4573
  anchor;
4481
4574
  authorizedAnchor;
4482
4575
  trustedHolders;
@@ -4485,6 +4578,10 @@ var TrustContext = class _TrustContext {
4485
4578
  keyStatementDigest;
4486
4579
  requiredConsensus;
4487
4580
  validUntil;
4581
+ /** Attach only profiles established by a verified transition; the bootstrap keeps the first epoch. */
4582
+ static withEpoch(reference, epoch, epochs = [epoch]) {
4583
+ return new _TrustContext(reference.anchor, reference.trustedHolders, reference.statement, reference.evaluatedAt, epoch, Object.freeze([...epochs]));
4584
+ }
4488
4585
  static create(anchor, statement, options) {
4489
4586
  const result = _TrustContext.evaluate(anchor, statement, options);
4490
4587
  if (result.context === void 0)
@@ -4507,11 +4604,11 @@ var TrustContext = class _TrustContext {
4507
4604
  let verificationError;
4508
4605
  try {
4509
4606
  for (const signature of statement.signatures) {
4510
- const key = pool.find((key2) => key2.id === signature.keyId);
4511
- if (key === void 0)
4607
+ const key2 = pool.find((key3) => key3.id === signature.keyId);
4608
+ if (key2 === void 0)
4512
4609
  throw new POIError("The public key of a signer was not provided");
4513
- statement.verifySignature(signature, key);
4514
- if (!key.isValidAt(statement.signedAt))
4610
+ statement.verifySignature(signature, key2);
4611
+ if (!key2.isValidAt(statement.signedAt))
4515
4612
  keysValid = false;
4516
4613
  }
4517
4614
  } catch (error) {
@@ -4657,10 +4754,10 @@ var Attestation = class _Attestation {
4657
4754
  return canonicalJSONBytes(json);
4658
4755
  }
4659
4756
  /** Verifies the signature with a trusted key, refusing one by another key. */
4660
- verifySignature(key) {
4661
- if (this.keyId !== key.id)
4757
+ verifySignature(key2) {
4758
+ if (this.keyId !== key2.id)
4662
4759
  throw new POIError("The attestation is signed by another key.");
4663
- verifySignature(this.algorithm, key.key, this.signingBytes(), this.value);
4760
+ verifySignature(this.algorithm, key2.key, this.signingBytes(), this.value);
4664
4761
  }
4665
4762
  /** Whether it is valid at the given point in time: signed then or before, and not after its `validUntil`. */
4666
4763
  isValidAt(time) {
@@ -4744,10 +4841,13 @@ var ChargingStationProof = class _ChargingStationProof {
4744
4841
  const { eTag, attested } = this.calculate();
4745
4842
  const { keyStatement, required: required2, signingKeyOf } = this.certifiedSigningKeys(basis, time);
4746
4843
  const certifiedSigningKeyOf = (attestation) => signingKeyOf(attestation.keyId, attestation.signedAt);
4844
+ let validUntil = keyStatement.validUntil;
4747
4845
  const counted = [];
4748
4846
  const holders = /* @__PURE__ */ new Set();
4749
4847
  const refused = [];
4848
+ if (basis instanceof TrustContext && basis.epoch.number > 1 && this.commitId.length !== { sha256: 64, sha384: 96, sha512: 128 }[basis.epoch.profiles.identificationHash]) throw new POIError("The station commit identification does not match its verified epoch profile");
4750
4849
  for (const attestation of this.attestations) {
4850
+ if (basis instanceof TrustContext && (!basis.epoch.profiles.allowsSignatureAlgorithm(attestation.algorithm) || attestation.signedAt < (basis.epoch.startedAt ?? -Infinity))) continue;
4751
4851
  if (attestation.roamingNetworkId !== networkId || attestation.commitId !== this.commitId || attestation.revision !== this.revision || attestation.eTag.algorithm !== this.hashAlgorithm)
4752
4852
  continue;
4753
4853
  if (attestation.subject !== void 0 || !bytesEqual(attestation.eTag.digest, eTag.digest)) {
@@ -4774,11 +4874,12 @@ var ChargingStationProof = class _ChargingStationProof {
4774
4874
  continue;
4775
4875
  }
4776
4876
  counted.push(attestation);
4877
+ validUntil = Math.min(validUntil, attestation.validUntil, certified.validUntil);
4777
4878
  holders.add(certified.holder);
4778
4879
  }
4779
4880
  if (holders.size < required2)
4780
4881
  throw new POIError(`Valid attestations of trusted keys of ${holders.size} holder${holders.size === 1 ? "" : "s"} attest the root ${etagToText(eTag)} of the proof, not of ${required2}!` + (refused.length > 0 ? ` (${refused.join("; ")})` : ""));
4781
- return { eTag, attested, attestations: counted, attestedBy: [...holders].sort(), keyStatement };
4882
+ return { eTag, attested, attestations: counted, attestedBy: [...holders].sort(), keyStatement, validUntil };
4782
4883
  }
4783
4884
  /**
4784
4885
  * Verifies a checkpoint against a trust anchor with this proof of the version of its commit (10.5), as
@@ -4850,8 +4951,12 @@ var ChargingStationProof = class _ChargingStationProof {
4850
4951
  if (certificate.signingKey.id !== keyId || certificate.roamingNetworkOperatorId !== operatorId || !certificate.signingKey.isValidAt(signedAt) || !certificate.signingKey.isValidAt(time) || !certificate.signingKey.maySign(KeyUsages.Party))
4851
4952
  continue;
4852
4953
  try {
4853
- certificate.verify(identityKeys, trustedIds);
4854
- return { key: certificate.signingKey, holder: required(holderOf.get(certificate.keyId), "The holder of a trusted identity key") };
4954
+ const identity = certificate.verify(identityKeys, trustedIds);
4955
+ return {
4956
+ key: certificate.signingKey,
4957
+ holder: required(holderOf.get(certificate.keyId), "The holder of a trusted identity key"),
4958
+ validUntil: Math.min(certificate.signingKey.notAfter ?? Infinity, identity.notAfter ?? Infinity)
4959
+ };
4855
4960
  } catch {
4856
4961
  continue;
4857
4962
  }
@@ -4877,7 +4982,7 @@ var ChargingStationProof = class _ChargingStationProof {
4877
4982
  };
4878
4983
 
4879
4984
  // src/proofs/priceStatement.ts
4880
- var PRICE_STATEMENT_CONTEXT = CONTEXT_PREFIX + "roamingNetwork/priceStatement/v1";
4985
+ var PRICE_STATEMENT_CONTEXT2 = CONTEXT_PREFIX + "roamingNetwork/priceStatement/v1";
4881
4986
  var PRICE_STATEMENT_PROOF_CONTEXT = CONTEXT_PREFIX + "roamingNetwork/priceStatementProof/v1";
4882
4987
  var PRICE_STATEMENT_ENTITY_TYPES = ["ChargingPool", "ChargingStation", "EVSE"];
4883
4988
  var MAX_PRICE_STATEMENT_SEQUENCE = Number.MAX_SAFE_INTEGER;
@@ -4919,7 +5024,7 @@ var PriceStatement = class _PriceStatement {
4919
5024
  json;
4920
5025
  static parse(value) {
4921
5026
  const given2 = requireObject(value, "A price statement");
4922
- checkContext(given2, PRICE_STATEMENT_CONTEXT, "a price statement");
5027
+ checkContext(given2, PRICE_STATEMENT_CONTEXT2, "a price statement");
4923
5028
  onlyProperties(given2, [
4924
5029
  "@context",
4925
5030
  "roamingNetworkId",
@@ -4938,7 +5043,7 @@ var PriceStatement = class _PriceStatement {
4938
5043
  "keyId",
4939
5044
  "value"
4940
5045
  ], "A price statement");
4941
- const json = { ...given2, "@context": PRICE_STATEMENT_CONTEXT };
5046
+ const json = { ...given2, "@context": PRICE_STATEMENT_CONTEXT2 };
4942
5047
  const entityType = requireString(json, "entityType", "The entity type of a price statement");
4943
5048
  if (!PRICE_STATEMENT_ENTITY_TYPES.includes(entityType))
4944
5049
  throw new POIError(`A price statement is one of a ChargingPool, a ChargingStation or an EVSE, not of a '${entityType}'!`);
@@ -5013,19 +5118,19 @@ var PriceStatement = class _PriceStatement {
5013
5118
  throw new POIError(`The price statement is one of the roaming network '${this.roamingNetworkId}', not of '${roamingNetworkId}'!`);
5014
5119
  if (this.signer !== operatorId || !this.entityId.startsWith(operatorId + "*"))
5015
5120
  throw new POIError(`The price statement of the ${this.entityType} '${this.entityId}' is not signed by its operator '${operatorId}'!`);
5016
- const key = signingKeys.find((signingKey) => signingKey.id === this.keyId);
5017
- if (key === void 0)
5121
+ const key2 = signingKeys.find((signingKey) => signingKey.id === this.keyId);
5122
+ if (key2 === void 0)
5018
5123
  throw new POIError(`The key '${this.keyId}' is no signing key of the charging station operator '${operatorId}'!`);
5019
- if (!key.isValidAt(this.signedAt))
5020
- throw new POIError(`The signing key '${key.id}' of the charging station operator '${operatorId}' was not valid when the price statement was signed!`);
5021
- if (!key.maySign(KeyUsages.PriceStatements))
5022
- throw new POIError(`The signing key '${key.id}' of the charging station operator '${operatorId}' may not sign 'priceStatements', only '${key.usages.join(", ")}'!`);
5124
+ if (!key2.isValidAt(this.signedAt))
5125
+ throw new POIError(`The signing key '${key2.id}' of the charging station operator '${operatorId}' was not valid when the price statement was signed!`);
5126
+ if (!key2.maySign(KeyUsages.PriceStatements))
5127
+ throw new POIError(`The signing key '${key2.id}' of the charging station operator '${operatorId}' may not sign 'priceStatements', only '${key2.usages.join(", ")}'!`);
5023
5128
  try {
5024
- verifySignature(this.algorithm, key.key, this.signingBytes(), this.value);
5129
+ verifySignature(this.algorithm, key2.key, this.signingBytes(), this.value);
5025
5130
  } catch (e) {
5026
5131
  throw new POIError(`The signature of the price statement is invalid: ${e instanceof Error ? e.message : String(e)}`);
5027
5132
  }
5028
- return key;
5133
+ return key2;
5029
5134
  }
5030
5135
  /** Whether it is valid at the given point in time: from `validFrom`, until `validUntil` where it names one. */
5031
5136
  isValidAt(time) {
@@ -5121,91 +5226,6 @@ var PriceStatementProof = class _PriceStatementProof {
5121
5226
  }
5122
5227
  };
5123
5228
 
5124
- // src/history/epoch.ts
5125
- var EPOCH_PROFILES_CONTEXT = CONTEXT_PREFIX + "roamingNetwork/epochProfiles/v1";
5126
- var EPOCH_CONTEXT = CONTEXT_PREFIX + "roamingNetwork/epoch/v1";
5127
- var EPOCH_LINK_CONTEXT = CONTEXT_PREFIX + "roamingNetwork/epochLink/v1";
5128
- var STYX_CANONICAL_JSON = "styx-canonical-json-v1";
5129
- var COMMIT_CONTEXT = CONTEXT_PREFIX + "roamingNetwork/commit/v3";
5130
- var CHECKPOINT_CONTEXT = CONTEXT_PREFIX + "roamingNetwork/checkpoint/v1";
5131
- var PRICE_STATEMENT_CONTEXT2 = CONTEXT_PREFIX + "roamingNetwork/priceStatement/v1";
5132
- var EpochProfiles = class _EpochProfiles {
5133
- constructor(identificationHash, signatureAlgorithms) {
5134
- this.identificationHash = identificationHash;
5135
- this.signatureAlgorithms = signatureAlgorithms;
5136
- }
5137
- identificationHash;
5138
- signatureAlgorithms;
5139
- /** The profiles of the first epoch: SHA-256 identifications, every signature algorithm. */
5140
- static FIRST = new _EpochProfiles("sha256", []);
5141
- /** Reads profiles as `RoamingNetworkEpochProfiles.TryParse` does, refusing those this library can not calculate. */
5142
- static parse(value) {
5143
- const json = requireObject(value, "The profiles of an epoch");
5144
- onlyProperties(json, ["@context", "canonicalization", "identificationHash", "commit", "checkpoint", "priceStatement", "signatureAlgorithms"], "The profiles of an epoch");
5145
- if (get(json, "@context") !== void 0 && get(json, "@context") !== EPOCH_PROFILES_CONTEXT)
5146
- throw new POIError(`The JSON-LD context of the profiles of an epoch must be '${EPOCH_PROFILES_CONTEXT}'!`);
5147
- if (get(json, "canonicalization") !== STYX_CANONICAL_JSON)
5148
- throw new POIError(`The canonicalization of an epoch must be '${STYX_CANONICAL_JSON}', the one this library calculates!`);
5149
- const identificationHash = get(json, "identificationHash");
5150
- if (!isHashAlgorithm(identificationHash))
5151
- throw new POIError("The identification hash of an epoch must be sha256, sha384 or sha512!");
5152
- if (get(json, "commit") !== COMMIT_CONTEXT || get(json, "checkpoint") !== CHECKPOINT_CONTEXT || get(json, "priceStatement") !== PRICE_STATEMENT_CONTEXT2)
5153
- throw new POIError(`The JSON-LD contexts of commits, checkpoints and price statements of an epoch must be '${COMMIT_CONTEXT}', '${CHECKPOINT_CONTEXT}' and '${PRICE_STATEMENT_CONTEXT2}', the ones this library calculates!`);
5154
- const algorithmsJSON = get(json, "signatureAlgorithms");
5155
- const algorithms = [];
5156
- if (algorithmsJSON !== void 0) {
5157
- if (!isArray(algorithmsJSON) || !algorithmsJSON.every((name) => typeof name === "string"))
5158
- throw new POIError("The signature algorithms of an epoch must be a JSON array of COSE names!");
5159
- for (const name of algorithmsJSON) {
5160
- signatureAlgorithm(name);
5161
- if (!algorithms.includes(name))
5162
- algorithms.push(name);
5163
- }
5164
- }
5165
- return new _EpochProfiles(identificationHash, algorithms.sort());
5166
- }
5167
- /** Whether the epoch accepts signatures of the algorithm. */
5168
- allowsSignatureAlgorithm(algorithm) {
5169
- return this.signatureAlgorithms.length === 0 ? SIGNATURE_ALGORITHMS.includes(algorithm) : this.signatureAlgorithms.includes(algorithm);
5170
- }
5171
- toJSON() {
5172
- return {
5173
- canonicalization: STYX_CANONICAL_JSON,
5174
- identificationHash: this.identificationHash,
5175
- commit: COMMIT_CONTEXT,
5176
- checkpoint: CHECKPOINT_CONTEXT,
5177
- priceStatement: PRICE_STATEMENT_CONTEXT2,
5178
- signatureAlgorithms: [...this.signatureAlgorithms]
5179
- };
5180
- }
5181
- toString() {
5182
- return `${this.identificationHash}, ${this.signatureAlgorithms.length === 0 ? "every signature algorithm" : this.signatureAlgorithms.join(", ")}`;
5183
- }
5184
- };
5185
- var FIRST_EPOCH = { number: 1, profiles: EpochProfiles.FIRST };
5186
- function parseEpoch(value, what = "An epoch") {
5187
- const json = requireObject(value, what);
5188
- onlyProperties(json, ["@context", "epoch", "profiles", "previous", "previousProfiles"], what);
5189
- if (get(json, "@context") !== void 0 && get(json, "@context") !== EPOCH_CONTEXT)
5190
- throw new POIError(`The JSON-LD context of the epoch must be '${EPOCH_CONTEXT}'!`);
5191
- return {
5192
- number: requireInteger(json, "epoch", 1, 4294967295, `The number of ${what.toLowerCase()}`),
5193
- profiles: EpochProfiles.parse(get(json, "profiles"))
5194
- };
5195
- }
5196
- var sameEpoch = (left, right) => left.number === right.number && left.profiles.identificationHash === right.profiles.identificationHash && left.profiles.signatureAlgorithms.join() === right.profiles.signatureAlgorithms.join();
5197
- function parseEpochLink(value) {
5198
- const json = requireObject(value, "The epoch before");
5199
- onlyProperties(json, ["@context", "epoch", "checkpointId", "eTags"], "The epoch before");
5200
- if (get(json, "@context") !== void 0 && get(json, "@context") !== EPOCH_LINK_CONTEXT)
5201
- throw new POIError(`The JSON-LD context of the epoch before must be '${EPOCH_LINK_CONTEXT}'!`);
5202
- return {
5203
- epoch: requireInteger(json, "epoch", 1, 4294967295, "The number of the epoch before"),
5204
- checkpointId: requireString(json, "checkpointId", "The final checkpoint of the epoch before"),
5205
- eTags: parseETags(get(json, "eTags"), true, "The ETags of the epoch before")
5206
- };
5207
- }
5208
-
5209
5229
  // src/history/changeSet.ts
5210
5230
  var CHANGE_SET_CONTEXT = CONTEXT_PREFIX + "roamingNetwork/changeSet/v3";
5211
5231
  var MERGE_CHANGE_SET_ID_PREFIX = "merge-";
@@ -5223,7 +5243,7 @@ function checkRebasedFrom(json, id) {
5223
5243
  throw new POIError(`The rebasedFrom of the change set '${id}' must be an array of the change sets it was rebased from!`);
5224
5244
  rebasedFrom.forEach((entry, index) => {
5225
5245
  const malformed = `Entry ${index} of the rebasedFrom of the change set '${id}' must name the @id, the beforeETags and the afterETags of a change set it was rebased from!`;
5226
- if (!isObject(entry) || Object.keys(entry).some((key) => key !== "@id" && key !== "beforeETags" && key !== "afterETags"))
5246
+ if (!isObject(entry) || Object.keys(entry).some((key2) => key2 !== "@id" && key2 !== "beforeETags" && key2 !== "afterETags"))
5227
5247
  throw new POIError(malformed);
5228
5248
  const namedId = get(entry, "@id");
5229
5249
  const beforeETags = get(entry, "beforeETags");
@@ -5366,10 +5386,10 @@ var ChangeSet = class _ChangeSet {
5366
5386
  });
5367
5387
  }
5368
5388
  /** Verifies one signature with the given key. */
5369
- verifySignature(signature, key) {
5370
- if (signature.keyId !== key.id)
5389
+ verifySignature(signature, key2) {
5390
+ if (signature.keyId !== key2.id)
5371
5391
  throw new POIError("The signature key ID does not match the key ID.");
5372
- verifySignature(signature.algorithm, key.key, this.signingBytes(signature), signature.value);
5392
+ verifySignature(signature.algorithm, key2.key, this.signingBytes(signature), signature.value);
5373
5393
  }
5374
5394
  };
5375
5395
 
@@ -5473,8 +5493,9 @@ var Commit = class _Commit {
5473
5493
  throw new POIError("The change set must be a JSON object!");
5474
5494
  if (parentIds.length === 0 !== (changeSet === void 0))
5475
5495
  throw new POIError(`The commit '${id}' must have parents and a change set, or neither, as a genesis commit!`);
5476
- if (!eTags.some((eTag) => eTag.format === "effectiveTree"))
5477
- throw new POIError(`The commit '${id}' names no effectiveTree ETag!`);
5496
+ for (const format of ["json", "cbor", "effectiveTree"])
5497
+ if (!eTags.some((eTag) => eTag.format === format))
5498
+ throw new POIError(`The commit '${id}' names no ${format} ETag: a commit requires the json, cbor and effectiveTree ETags of its version!`);
5478
5499
  const signaturesJSON = get(json, "signatures");
5479
5500
  const signatures = signaturesJSON === void 0 ? [] : requireArray(signaturesJSON, "The signatures of a commit").map((signature) => parseRoleSignature(signature, "A signature of a commit"));
5480
5501
  const identificationHash = hashAlgorithmOfIdentification(id);
@@ -5531,10 +5552,10 @@ var Commit = class _Commit {
5531
5552
  return roleSignatureBytes(COMMIT_SIGNATURE_CONTEXT, signature, "commit", this.signedContent());
5532
5553
  }
5533
5554
  /** Verifies one signature with a trusted key, refusing one by another key. */
5534
- verifySignature(signature, key) {
5535
- if (signature.keyId !== key.id)
5555
+ verifySignature(signature, key2) {
5556
+ if (signature.keyId !== key2.id)
5536
5557
  throw new POIError("The signature key ID does not match the trusted key ID.");
5537
- verifySignature(signature.algorithm, key.key, this.signingBytes(signature), signature.value);
5558
+ verifySignature(signature.algorithm, key2.key, this.signingBytes(signature), signature.value);
5538
5559
  }
5539
5560
  /** The description of its change set, in its first language. */
5540
5561
  get description() {
@@ -6188,17 +6209,17 @@ function ownerOf(content, change, index, addedEnergyMeterChildren) {
6188
6209
  }
6189
6210
  const energyMeterChild = ENERGY_METER_CHILD_TYPES[change.entityType];
6190
6211
  if (energyMeterChild !== void 0) {
6191
- const key = `${change.entityType}:${change.entityId.toUpperCase()}`;
6212
+ const key2 = `${change.entityType}:${change.entityId.toUpperCase()}`;
6192
6213
  let owner;
6193
6214
  if (change.kind === "Add") {
6194
6215
  if (change.parentEntityType !== "ChargingStationOperator" && change.parentEntityType !== "GridOperator" || change.parentEntityId === void 0)
6195
6216
  throw new POIError(`Change ${index}: adding the ${change.entityType} '${change.entityId}' needs its ChargingStationOperator or GridOperator as its parent!`);
6196
6217
  const party = partyNamed(content, change.parentEntityType, change.parentEntityId);
6197
6218
  owner = { type: change.parentEntityType, id: asText(party?.["@id"] ?? change.parentEntityId) };
6198
- addedEnergyMeterChildren.set(key, owner);
6219
+ addedEnergyMeterChildren.set(key2, owner);
6199
6220
  } else {
6200
6221
  const found = energyMeterChildOf(content, energyMeterChild, change.entityId);
6201
- owner = addedEnergyMeterChildren.get(key) ?? (found === void 0 ? void 0 : { type: found.partyType, id: asText(found.party["@id"]) });
6222
+ owner = addedEnergyMeterChildren.get(key2) ?? (found === void 0 ? void 0 : { type: found.partyType, id: asText(found.party["@id"]) });
6202
6223
  }
6203
6224
  if (owner === void 0)
6204
6225
  throw new POIError(`Change ${index}: the ${change.entityType} '${change.entityId}' belongs to no charging station operator and no grid operator!`);
@@ -6265,40 +6286,40 @@ function checkOwnership(before, changeSet) {
6265
6286
  if (keys === void 0)
6266
6287
  return;
6267
6288
  const known = change.entityType === "RoamingNetworkOperator" ? rnoIdentity : parties.get(`${change.entityType}:${change.entityId}`)?.identityKeys ?? [];
6268
- for (const key of POIPublicKey.parseAll(keys, `identity keys of change ${index}`))
6269
- if (!known.some((knownKey) => knownKey.id === key.id))
6270
- newIdentityKeys.push(key);
6289
+ for (const key2 of POIPublicKey.parseAll(keys, `identity keys of change ${index}`))
6290
+ if (!known.some((knownKey) => knownKey.id === key2.id))
6291
+ newIdentityKeys.push(key2);
6271
6292
  });
6272
6293
  const allKeys = /* @__PURE__ */ new Map();
6273
- for (const key of [...rnoSigning, ...rnoIdentity, ...[...parties.values()].flatMap((party) => [...party.signingKeys, ...party.identityKeys]), ...newIdentityKeys])
6274
- allKeys.set(key.id, key);
6294
+ for (const key2 of [...rnoSigning, ...rnoIdentity, ...[...parties.values()].flatMap((party) => [...party.signingKeys, ...party.identityKeys]), ...newIdentityKeys])
6295
+ allKeys.set(key2.id, key2);
6275
6296
  const partyKeys = [
6276
- ...rno === void 0 ? [] : [...rnoSigning, ...rnoIdentity].map((key) => ({ party: `RoamingNetworkOperator '${rno.id}'`, key })),
6277
- ...[...parties.values()].flatMap((party) => [...party.signingKeys, ...party.identityKeys].map((key) => ({ party: `${party.type} '${party.id}'`, key })))
6297
+ ...rno === void 0 ? [] : [...rnoSigning, ...rnoIdentity].map((key2) => ({ party: `RoamingNetworkOperator '${rno.id}'`, key: key2 })),
6298
+ ...[...parties.values()].flatMap((party) => [...party.signingKeys, ...party.identityKeys].map((key2) => ({ party: `${party.type} '${party.id}'`, key: key2 })))
6278
6299
  ];
6279
- const valid = /* @__PURE__ */ new Set();
6300
+ const valid2 = /* @__PURE__ */ new Set();
6280
6301
  changeSet.signatures.forEach((signature, index) => {
6281
- const key = allKeys.get(signature.keyId);
6282
- if (key === void 0)
6302
+ const key2 = allKeys.get(signature.keyId);
6303
+ if (key2 === void 0)
6283
6304
  throw new POIError(`Signature ${index} is by the key '${signature.keyId}', which is no key of a party of this roaming network!`);
6284
6305
  for (const partyKey of partyKeys.filter((item) => item.key.id === signature.keyId))
6285
6306
  if (!partyKey.key.isValidAt(createdAt))
6286
6307
  throw new POIError(`Signature ${index}: the key '${partyKey.key.id}' of the ${partyKey.party} is not valid at ${toTimestamp(createdAt)}!`);
6287
6308
  try {
6288
- changeSet.verifySignature(signature, key);
6309
+ changeSet.verifySignature(signature, key2);
6289
6310
  } catch (e) {
6290
6311
  throw new POIError(`Signature ${index} ('${signature.keyId}') is invalid: ${e instanceof Error ? e.message : String(e)}`);
6291
6312
  }
6292
- valid.add(signature.keyId);
6313
+ valid2.add(signature.keyId);
6293
6314
  });
6294
- for (const key of newIdentityKeys)
6295
- if (!valid.has(key.id))
6296
- throw new POIError(`The new identity key '${key.id}' did not sign the change set: no proof of possession!`);
6297
- const signedValid = (key) => valid.has(key.id) && key.isValidAt(createdAt);
6315
+ for (const key2 of newIdentityKeys)
6316
+ if (!valid2.has(key2.id))
6317
+ throw new POIError(`The new identity key '${key2.id}' did not sign the change set: no proof of possession!`);
6318
+ const signedValid = (key2) => valid2.has(key2.id) && key2.isValidAt(createdAt);
6298
6319
  const rnoHolders = (keys, ...usages) => new Set([
6299
- ...keys !== "identity" ? rnoSigning.filter((key) => [KeyUsages.Party, ...usages].some((usage) => key.maySign(usage))) : [],
6320
+ ...keys !== "identity" ? rnoSigning.filter((key2) => [KeyUsages.Party, ...usages].some((usage) => key2.maySign(usage))) : [],
6300
6321
  ...keys !== "signing" ? rnoIdentity : []
6301
- ].filter(signedValid).map((key) => key.holderOrUnnamed));
6322
+ ].filter(signedValid).map((key2) => key2.holderOrUnnamed));
6302
6323
  changeSet.changes.forEach((change, index) => {
6303
6324
  checkPartyChange(change, index);
6304
6325
  const owner = ownerOf(before.content, change, index, addedEnergyMeterChildren);
@@ -6313,7 +6334,7 @@ function checkOwnership(before, changeSet) {
6313
6334
  const party = parties.get(`${owner.type}:${owner.id}`);
6314
6335
  if (party === void 0)
6315
6336
  throw new POIError(`${describeChange(change, index)}: the ${owner.type} '${owner.id}' does not exist!`);
6316
- const bySigningKey = owner.keys !== "identity" && party.signingKeys.some((key) => key.maySign(owner.usage) && signedValid(key));
6337
+ const bySigningKey = owner.keys !== "identity" && party.signingKeys.some((key2) => key2.maySign(owner.usage) && signedValid(key2));
6317
6338
  const byIdentityKey = owner.keys !== "signing" && party.identityKeys.some(signedValid);
6318
6339
  const byRecovery = owner.keys !== "signing" && rnoHolders("identity").size >= minConsensus && rnoIdentity.some(signedValid);
6319
6340
  const byRNO = owner.keys === "signing" && party.signingKeys.length === 0 && rnoHolders("signing", owner.usage).size >= minConsensus;
@@ -6406,8 +6427,8 @@ function checkKeyHolders(before, after) {
6406
6427
  const keysOf2 = (network) => {
6407
6428
  const keys = /* @__PURE__ */ new Map();
6408
6429
  const add = (type, id, partyKeys) => {
6409
- for (const key of partyKeys)
6410
- keys.set(`${type}:${id}:${key.id}`, { party: `${type} '${id}'`, key });
6430
+ for (const key2 of partyKeys)
6431
+ keys.set(`${type}:${id}:${key2.id}`, { party: `${type} '${id}'`, key: key2 });
6411
6432
  };
6412
6433
  if (network.operator !== void 0)
6413
6434
  add("RoamingNetworkOperator", network.operator.id, [...network.operator.identityKeys, ...network.operator.signingKeyCertificates.map((certificate) => certificate.signingKey)]);
@@ -6416,10 +6437,10 @@ function checkKeyHolders(before, after) {
6416
6437
  return keys;
6417
6438
  };
6418
6439
  const holders = keysOf2(before);
6419
- for (const [where, { party, key }] of keysOf2(after)) {
6440
+ for (const [where, { party, key: key2 }] of keysOf2(after)) {
6420
6441
  const previous = holders.get(where);
6421
- if (previous !== void 0 && previous.key.holder !== key.holder)
6422
- throw new POIError(`The key '${key.id}' of the ${party} keeps its holder ${previous.key.holder === void 0 ? "none" : `'${previous.key.holder}'`}, not ${key.holder === void 0 ? "none" : `'${key.holder}'`}!`);
6442
+ if (previous !== void 0 && previous.key.holder !== key2.holder)
6443
+ throw new POIError(`The key '${key2.id}' of the ${party} keeps its holder ${previous.key.holder === void 0 ? "none" : `'${previous.key.holder}'`}, not ${key2.holder === void 0 ? "none" : `'${key2.holder}'`}!`);
6423
6444
  }
6424
6445
  }
6425
6446
 
@@ -6555,10 +6576,10 @@ var Checkpoint = class _Checkpoint {
6555
6576
  return roleSignatureBytes(CHECKPOINT_SIGNATURE_CONTEXT, signature, "checkpoint", this.signedContent());
6556
6577
  }
6557
6578
  /** Verifies one signature with a trusted key, refusing one by another key. */
6558
- verifySignature(signature, key) {
6559
- if (signature.keyId !== key.id)
6579
+ verifySignature(signature, key2) {
6580
+ if (signature.keyId !== key2.id)
6560
6581
  throw new POIError("The signature key ID does not match the trusted key ID.");
6561
- verifySignature(signature.algorithm, key.key, this.signingBytes(signature), signature.value);
6582
+ verifySignature(signature.algorithm, key2.key, this.signingBytes(signature), signature.value);
6562
6583
  }
6563
6584
  /** Whether the checkpoint names the given commit with its revision and ETags. */
6564
6585
  isOf(commit) {
@@ -7125,11 +7146,11 @@ var QIDigitalDocument = class {
7125
7146
  return canonicalJSONBytes(this.signatureInput());
7126
7147
  }
7127
7148
  /** Verifies its signature with the given public key: by that key, with a canonical algorithm and value (`VerifySignature`). */
7128
- verifySignature(key) {
7149
+ verifySignature(key2) {
7129
7150
  const signature = this.signature;
7130
7151
  if (signature === void 0)
7131
7152
  throw new POIError(`${this.documentText} is not signed.`);
7132
- if (signature.keyId !== key.id)
7153
+ if (signature.keyId !== key2.id)
7133
7154
  throw new POIError(`The key ID of ${this.documentText} does not match the expected key ID.`);
7134
7155
  try {
7135
7156
  signatureAlgorithm(signature.algorithm);
@@ -7144,7 +7165,7 @@ var QIDigitalDocument = class {
7144
7165
  }
7145
7166
  if (value.length === 0)
7146
7167
  throw new POIError("The value must contain nonempty canonical standard Base64.");
7147
- verifySignature(signature.algorithm, key.key, this.signingBytes(), signature.value);
7168
+ verifySignature(signature.algorithm, key2.key, this.signingBytes(), signature.value);
7148
7169
  }
7149
7170
  /**
7150
7171
  * Verifies its signature with the one of the given signing keys of its issuer it names, valid when it was signed, and
@@ -7154,7 +7175,7 @@ var QIDigitalDocument = class {
7154
7175
  const signature = this.signature;
7155
7176
  if (signature === void 0)
7156
7177
  throw new POIError(`${capitalized(this.documentText)} is not signed!`);
7157
- const signingKey = signingKeys.find((key) => key.id === signature.keyId);
7178
+ const signingKey = signingKeys.find((key2) => key2.id === signature.keyId);
7158
7179
  if (signingKey === void 0)
7159
7180
  throw new POIError(`The key '${signature.keyId}' of ${this.documentText} is no signing key of ${this.issuerText}!`);
7160
7181
  if (!signingKey.isValidAt(signature.signedAt))
@@ -8429,7 +8450,7 @@ var OperatorTrust = class _OperatorTrust {
8429
8450
  for (const [id, holder] of this.identityKeyHolders)
8430
8451
  known.set(holder, [...known.get(holder) ?? [], id]);
8431
8452
  const { signedBy, holderOf } = statement.verifyHolders([...known.values()], operator.identityKeys, { minConsensus: this.requiredConsensus, at: timeOf(at) });
8432
- const trusted = /* @__PURE__ */ new Set([...this.anchor.keys, ...statement.identityKeys.map((key) => key.id)]);
8453
+ const trusted = /* @__PURE__ */ new Set([...this.anchor.keys, ...statement.identityKeys.map((key2) => key2.id)]);
8433
8454
  return new _OperatorTrust(
8434
8455
  this.anchor,
8435
8456
  operator,
@@ -8492,11 +8513,11 @@ var OperatorTrust = class _OperatorTrust {
8492
8513
  verifyOperatorSignatures(signatures, verify, what) {
8493
8514
  const signedBy = /* @__PURE__ */ new Set();
8494
8515
  for (const signature of signatures.filter((signature2) => signature2.role === SignatureRoles.RoamingNetworkOperator)) {
8495
- const key = this.signingKeyOf(signature);
8516
+ const key2 = this.signingKeyOf(signature);
8496
8517
  const holder = this.holderOf(signature);
8497
- if (key === void 0 || holder === void 0)
8518
+ if (key2 === void 0 || holder === void 0)
8498
8519
  throw new POIError(`the key '${signature.keyId}' is no signing key a trusted identity key of its holder certifies`);
8499
- verify(signature, key);
8520
+ verify(signature, key2);
8500
8521
  signedBy.add(holder);
8501
8522
  }
8502
8523
  if (signedBy.size < this.requiredConsensus)
@@ -8531,8 +8552,8 @@ function verifyVersion(basis, networkJSON, commitJSON, at) {
8531
8552
  if (commit.roamingNetworkId !== network.id || !network.matchesCommitETags(commit.eTags))
8532
8553
  throw new POIError("the commit does not lead to the roaming network");
8533
8554
  const trust = OperatorTrust.of(basis, network, at);
8534
- const signedBy = trust.verifyOperatorSignatures(commit.signatures, (signature, key) => {
8535
- commit.verifySignature(signature, key);
8555
+ const signedBy = trust.verifyOperatorSignatures(commit.signatures, (signature, key2) => {
8556
+ commit.verifySignature(signature, key2);
8536
8557
  }, "the commit");
8537
8558
  return {
8538
8559
  network,
@@ -8691,6 +8712,10 @@ function verifyChargingStation(anchor, proof, options = {}) {
8691
8712
  revision: parsed.revision,
8692
8713
  eTag: verified.eTag,
8693
8714
  attestedBy: verified.attestedBy,
8715
+ verification: {
8716
+ at: anchor instanceof TrustContext ? anchor.evaluatedAt : timeOf(options.at),
8717
+ validUntil: verified.validUntil
8718
+ },
8694
8719
  warnings
8695
8720
  };
8696
8721
  }
@@ -8709,7 +8734,7 @@ function verifyPriceStatements(station, statements, options = {}) {
8709
8734
  const signedWith = /* @__PURE__ */ new Map();
8710
8735
  for (const item of list) {
8711
8736
  const statement = PriceStatement.parse(item);
8712
- const key = `${statement.entityType}:${statement.entityId}`;
8737
+ const key2 = `${statement.entityType}:${statement.entityId}`;
8713
8738
  const entity = entities.get(statement.chain);
8714
8739
  if (entity === void 0)
8715
8740
  throw new POIError(`The price statement of the ${statement.entityType} '${statement.entityId}' is none of the station, its pool or its EVSEs!`);
@@ -8721,23 +8746,23 @@ function verifyPriceStatements(station, statements, options = {}) {
8721
8746
  if (!entity.tariffs.has(asText(price["chargingTariffId"])))
8722
8747
  throw new POIError(`The price statement of the EVSE '${statement.entityId}' names the charging tariff '${asText(price["chargingTariffId"])}', which is not one of its own!`);
8723
8748
  }
8724
- const known = byEntity.get(key) ?? [];
8749
+ const known = byEntity.get(key2) ?? [];
8725
8750
  if (!known.some((other) => other.digestHex() === statement.digestHex()))
8726
- byEntity.set(key, [...known, statement]);
8751
+ byEntity.set(key2, [...known, statement]);
8727
8752
  }
8728
8753
  const current = /* @__PURE__ */ new Map();
8729
- for (const [key, chain] of byEntity) {
8754
+ for (const [key2, chain] of byEntity) {
8730
8755
  chain.sort((left, right) => left.sequence - right.sequence);
8731
8756
  if (new Set(chain.map((statement) => statement.sequence)).size !== chain.length)
8732
- throw new POIError(`Two price statements of one sequence of '${key}': a split view!`);
8757
+ throw new POIError(`Two price statements of one sequence of '${key2}': a split view!`);
8733
8758
  chain.forEach((statement, index) => {
8734
8759
  const before = chain[index - 1];
8735
8760
  if (before !== void 0 && statement.sequence === before.sequence + 1)
8736
8761
  verifyPriceStatementChain([before, statement]);
8737
8762
  });
8738
- const valid = [...chain].reverse().find((statement) => statement.validFrom <= time);
8739
- if (valid?.isValidAt(time) === true && signedWith.get(valid.digestHex())?.isValidAt(time) === true)
8740
- current.set(key, { statement: valid, digest: valid.digestHex() });
8763
+ const valid2 = [...chain].reverse().find((statement) => statement.validFrom <= time);
8764
+ if (valid2?.isValidAt(time) === true && signedWith.get(valid2.digestHex())?.isValidAt(time) === true)
8765
+ current.set(key2, { statement: valid2, digest: valid2.digestHex() });
8741
8766
  }
8742
8767
  return current;
8743
8768
  }
@@ -8780,14 +8805,14 @@ function verifyCheckpointSignatures(checkpoint, trust, options = {}) {
8780
8805
  const ignored = [];
8781
8806
  for (const signature of checkpoint.signatures) {
8782
8807
  if (signature.role === SignatureRoles.RoamingNetworkOperator) {
8783
- const key = trust.signingKeyOf(signature);
8808
+ const key2 = trust.signingKeyOf(signature);
8784
8809
  const holder = trust.holderOf(signature);
8785
- if (key === void 0 || holder === void 0) {
8810
+ if (key2 === void 0 || holder === void 0) {
8786
8811
  ignored.push(`the key '${signature.keyId}' is no signing key a trusted identity key of its holder certifies`);
8787
8812
  continue;
8788
8813
  }
8789
8814
  try {
8790
- checkpoint.verifySignature(signature, key);
8815
+ checkpoint.verifySignature(signature, key2);
8791
8816
  signedBy.add(holder);
8792
8817
  } catch (e) {
8793
8818
  ignored.push(`the signature by '${signature.keyId}' is invalid: ${e instanceof Error ? e.message : String(e)}`);
@@ -9008,11 +9033,11 @@ var RuntimeStatus = class _RuntimeStatus {
9008
9033
  }
9009
9034
  /** Takes a status update: `Updated` unless the status it changes is a later one already, `Outdated` then. */
9010
9035
  apply(update) {
9011
- const key = _RuntimeStatus.key(update.entityType, update.entityId, update.kind);
9012
- const current = this.entries.get(key);
9036
+ const key2 = _RuntimeStatus.key(update.entityType, update.entityId, update.kind);
9037
+ const current = this.entries.get(key2);
9013
9038
  if (current !== void 0 && current.timestamp > update.timestamp)
9014
9039
  return "Outdated";
9015
- this.entries.set(key, { value: update.value, timestamp: update.timestamp });
9040
+ this.entries.set(key2, { value: update.value, timestamp: update.timestamp });
9016
9041
  return "Updated";
9017
9042
  }
9018
9043
  get(entityType, entityId2, kind = "status") {
@@ -9022,7 +9047,1108 @@ var RuntimeStatus = class _RuntimeStatus {
9022
9047
  return this.entries.size;
9023
9048
  }
9024
9049
  };
9050
+ var MeasurementKeyError = class extends POIError {
9051
+ constructor(code, message) {
9052
+ super(message);
9053
+ this.code = code;
9054
+ }
9055
+ code;
9056
+ };
9057
+ function parseMeasurementPublicKey(value, format) {
9058
+ if (!["spki-der", "cose-key"].includes(format))
9059
+ throw new MeasurementKeyError("unsupported-key", "Unknown measurement-key format");
9060
+ const json = requireObject(value, "A measurement public key");
9061
+ const id = requireString(json, "@id");
9062
+ const bytes = fromBase64(requireString(json, "key"));
9063
+ let cose;
9064
+ let idScheme;
9065
+ try {
9066
+ if (format === "spki-der") {
9067
+ const document = new DerReader(bytes, "Measurement SubjectPublicKeyInfo");
9068
+ const sequence = contentsOf(document.read(Tag.sequence), "SubjectPublicKeyInfo");
9069
+ document.expectEnd();
9070
+ const algorithm = contentsOf(sequence.read(Tag.sequence), "AlgorithmIdentifier");
9071
+ const oid = derObjectIdentifier(algorithm.read(Tag.objectIdentifier), "Public key algorithm");
9072
+ const curve = derObjectIdentifier(algorithm.read(Tag.objectIdentifier), "Public key curve");
9073
+ algorithm.expectEnd();
9074
+ if (oid !== "1.2.840.10045.2.1" || curve !== "1.2.840.10045.3.1.7")
9075
+ throw new MeasurementKeyError("unsupported-key", "The measurement-key pilot supports ECDSA P-256 only");
9076
+ const point = derBitString(sequence.read(Tag.bitString), "Measurement public point");
9077
+ sequence.expectEnd();
9078
+ if (point.length !== 65 || point[0] !== 4 || !isOnCurve(CoseCurves.P256, point))
9079
+ throw new MeasurementKeyError("invalid-key", "A measurement key needs a valid uncompressed P-256 point");
9080
+ cose = CoseKey.fromCoordinates(CoseCurves.P256, point.slice(1, 33), point.slice(33));
9081
+ idScheme = "sha256-spki";
9082
+ if (id !== toHex(hash("sha256", bytes))) throw new MeasurementKeyError("invalid-key", "The measurement SPKI hash does not match its ID");
9083
+ } else {
9084
+ cose = CoseKey.parse(bytes);
9085
+ if (cose.isPrivate || cose.curve?.id !== CoseCurves.P256.id || cose.x?.length !== 32 || cose.y?.length !== 32)
9086
+ throw new MeasurementKeyError("unsupported-key", "A public P-256 COSE measurement key is required");
9087
+ const point = Uint8Array.from([4, ...cose.x, ...cose.y]);
9088
+ if (!isOnCurve(CoseCurves.P256, point)) throw new MeasurementKeyError("invalid-key", "The measurement point is not on P-256");
9089
+ if (toBase64(cose.toBytes()) !== toBase64(bytes) || id !== toHex(cose.thumbprint()))
9090
+ throw new MeasurementKeyError("invalid-key", "The COSE key must be deterministic and match its thumbprint");
9091
+ idScheme = "sha256-cose-key-thumbprint";
9092
+ cose = CoseKey.fromCoordinates(CoseCurves.P256, cose.x, cose.y);
9093
+ }
9094
+ } catch (error) {
9095
+ if (error instanceof MeasurementKeyError) throw error;
9096
+ throw new MeasurementKeyError("invalid-key", error instanceof Error ? error.message : "Invalid measurement key");
9097
+ }
9098
+ return Object.freeze({
9099
+ id,
9100
+ idScheme,
9101
+ canonicalId: toHex(cose.thumbprint()),
9102
+ sourceFormat: format,
9103
+ value: toBase64(cose.toBytes()),
9104
+ encodings: Object.freeze(["COSE_Key", "CBOR", "base64"]),
9105
+ algorithm: "ECDSA-secp256r1"
9106
+ });
9107
+ }
9108
+ function verifyMeterKey(context, proof, request) {
9109
+ if (!(context instanceof TrustContext)) throw new POIError("Meter-key authorization requires a TrustContext");
9110
+ const station = verifyChargingStation(context, proof, { evseId: request.evseId });
9111
+ if (station.verification.validUntil < context.evaluatedAt)
9112
+ throw new POIError("The certified-key observation is no longer current");
9113
+ const evse = station.evses.find((item) => item["@id"] === request.evseId);
9114
+ const meter = evse?.["energyMeter"];
9115
+ if (!isObject(meter)) throw new MeasurementKeyError("missing-meter", "The selected EVSE supplies no directly associated energy meter");
9116
+ const meterId = requireString(meter, "@id");
9117
+ const serial = requireString(meter, "serialNumber");
9118
+ if (request.meterSerialNumber.trim() === "" || serial !== request.meterSerialNumber || request.meterId !== void 0 && meterId !== request.meterId)
9119
+ throw new MeasurementKeyError("identity-mismatch", "The signed meter identity does not match the selected EVSE meter");
9120
+ const all = meter["publicKeys"];
9121
+ const keys = Array.isArray(all) ? all.filter((item) => request.keyId === void 0 || isObject(item) && item["@id"] === request.keyId) : [];
9122
+ if (keys.length !== 1) throw new MeasurementKeyError("ambiguous-key", "Select exactly one explicitly identified meter key; no trial verification across keys");
9123
+ const key2 = parseMeasurementPublicKey(keys[0] ?? null, request.keyFormat);
9124
+ return Object.freeze({
9125
+ evseId: request.evseId,
9126
+ chargingStationId: requireString(station.chargingStation, "@id"),
9127
+ meterId,
9128
+ meterSerialNumber: serial,
9129
+ key: key2,
9130
+ evidence: Object.freeze({
9131
+ roamingNetworkId: context.roamingNetworkId,
9132
+ roamingNetworkOperatorId: context.roamingNetworkOperatorId,
9133
+ commitId: station.commitId,
9134
+ revision: station.revision,
9135
+ treeRoot: etagToText(station.eTag),
9136
+ keyStatementDigest: context.keyStatementDigest,
9137
+ verifiedAt: station.verification.at,
9138
+ validUntil: station.verification.validUntil,
9139
+ association: "current-snapshot"
9140
+ })
9141
+ });
9142
+ }
9143
+
9144
+ // src/verifier/historicalMeasurements.ts
9145
+ var HISTORICAL_METER_EVIDENCE_CONTEXT = "https://open.charging.cloud/contexts/wwcp/historicalMeterEvidence/v1";
9146
+ function parseHistoricalMeterEvidence(value) {
9147
+ const json = requireObject(value, "Historical meter evidence");
9148
+ onlyProperties(json, ["@context", "EVSEId", "versions"], "Historical meter evidence");
9149
+ if (json["@context"] !== HISTORICAL_METER_EVIDENCE_CONTEXT) throw new POIError("Unsupported historical meter evidence");
9150
+ const versions = requireArray(json["versions"], "Historical station versions").map((item) => {
9151
+ const version = requireObject(item, "A historical station version");
9152
+ onlyProperties(version, ["chargingStationProof", "commitProof", "checkpoints"], "A historical station version");
9153
+ return {
9154
+ proof: requireObject(version["chargingStationProof"], "A historical station proof"),
9155
+ ...version["commitProof"] === void 0 ? {} : { commitProof: version["commitProof"] },
9156
+ checkpoints: (version["checkpoints"] === void 0 ? [] : requireArray(version["checkpoints"], "Historical checkpoints")).map((item2) => {
9157
+ const entry = requireObject(item2, "A checkpoint with its signing authority");
9158
+ onlyProperties(entry, ["checkpoint", "operator"], "A historical checkpoint entry");
9159
+ return { checkpoint: requireObject(entry["checkpoint"], "A checkpoint"), operator: requireObject(entry["operator"], "Its operator") };
9160
+ })
9161
+ };
9162
+ });
9163
+ if (versions.length === 0) throw new POIError("Historical station evidence is empty");
9164
+ return { evseId: requireString(json, "EVSEId"), versions };
9165
+ }
9166
+ function historicalOperatorContext(current, operator, at) {
9167
+ const json = requireObject(operator, "A historical roaming network operator");
9168
+ const statement = KeyStatement.parse(json["keyStatement"]);
9169
+ if (statement.roamingNetworkId !== current.roamingNetworkId || statement.roamingNetworkOperatorId !== current.roamingNetworkOperatorId || json["@id"] !== current.roamingNetworkOperatorId)
9170
+ throw new POIError("The historical statement belongs to another authorized network/operator");
9171
+ const authority = current instanceof HistoricalTrustContext ? current.authorityAt(at) : void 0;
9172
+ const anchor = authority?.authorizedAnchor ?? current.authorizedAnchor;
9173
+ const created = TrustContext.create(anchor, statement, {
9174
+ trustedIdentityKeyIds: anchor.keys,
9175
+ otherIdentityKeys: POIPublicKey.parseAll(json["identityKeys"], "Historical operator identities"),
9176
+ at
9177
+ });
9178
+ const context = TrustContext.withEpoch(created, authority?.epoch ?? current.epoch, current.epochs);
9179
+ if (authority === void 0 && context.requiredConsensus < current.requiredConsensus) throw new POIError("Historical verification must not weaken the current local quorum");
9180
+ return context;
9181
+ }
9182
+ var HistoricalTrustContext = class _HistoricalTrustContext {
9183
+ constructor(reference, evaluatedAt, authorities = []) {
9184
+ this.evaluatedAt = evaluatedAt;
9185
+ this.authorities = authorities;
9186
+ this.epoch = reference.epoch;
9187
+ this.epochs = reference.epochs;
9188
+ this.authorizedAnchor = reference.authorizedAnchor;
9189
+ this.roamingNetworkId = reference.roamingNetworkId;
9190
+ this.roamingNetworkOperatorId = reference.roamingNetworkOperatorId;
9191
+ this.requiredConsensus = reference.requiredConsensus;
9192
+ this.keyStatementDigest = reference.keyStatementDigest;
9193
+ this.trustedHolders = reference.trustedHolders;
9194
+ this.validUntil = evaluatedAt + 6e4;
9195
+ Object.freeze(this);
9196
+ }
9197
+ evaluatedAt;
9198
+ authorities;
9199
+ authorizedAnchor;
9200
+ roamingNetworkId;
9201
+ roamingNetworkOperatorId;
9202
+ requiredConsensus;
9203
+ keyStatementDigest;
9204
+ /** An operation deadline, not historical document validity or a lease on local trust. */
9205
+ validUntil;
9206
+ trustedHolders;
9207
+ epoch;
9208
+ epochs;
9209
+ /** Contexts must be reconstructed from current local roots and verified transition certificates, never serialized success flags. */
9210
+ static fromVerifiedAuthorities(reference, authorities, now) {
9211
+ if (!Number.isFinite(now) || authorities.length === 0 || authorities.some((item) => item.context.roamingNetworkId !== reference.roamingNetworkId || item.context.roamingNetworkOperatorId !== reference.roamingNetworkOperatorId || !Number.isFinite(item.from) || item.until <= item.from)) throw new POIError("Invalid historical authority lineage");
9212
+ return new _HistoricalTrustContext(reference, now, authorities);
9213
+ }
9214
+ authorityAt(at) {
9215
+ if (this.authorities.length === 0) return void 0;
9216
+ const matches = this.authorities.filter((item) => at >= item.from && at < item.until);
9217
+ if (matches.length !== 1) throw new POIError("No unambiguous authorized policy covers this historical time");
9218
+ return matches[0]?.context;
9219
+ }
9220
+ static create(anchor, reference, options) {
9221
+ const now = options.now ?? Date.now();
9222
+ if (!Number.isFinite(now) || reference.signedAt > now) throw new POIError("Invalid historical authorization/reference time");
9223
+ const verified = TrustContext.create(anchor, reference, {
9224
+ trustedIdentityKeyIds: options.trustedIdentityKeyIds,
9225
+ ...options.otherIdentityKeys === void 0 ? {} : { otherIdentityKeys: options.otherIdentityKeys },
9226
+ at: reference.signedAt
9227
+ });
9228
+ return new _HistoricalTrustContext(verified, now);
9229
+ }
9230
+ };
9231
+ function verifyHistoricalMeterEvidence(current, value, request, policy = {}) {
9232
+ if (!(current instanceof TrustContext) && !(current instanceof HistoricalTrustContext)) throw new POIError("Historical verification requires current local authorization");
9233
+ const requiredWitnesses = policy.requiredWitnesses ?? 0;
9234
+ if (!Number.isSafeInteger(requiredWitnesses) || requiredWitnesses < 0) throw new POIError("Invalid historical witness policy");
9235
+ for (const revocation of policy.revocations ?? []) if (!Number.isFinite(revocation.invalidFrom)) throw new POIError("Invalid compromise time");
9236
+ const revoked = (id, at) => (policy.revocations ?? []).some((item) => item.keyId === id && at >= item.invalidFrom);
9237
+ const evidence = parseHistoricalMeterEvidence(value);
9238
+ if (evidence.evseId !== request.evseId) throw new POIError("Historical evidence belongs to another EVSE");
9239
+ const times = [...new Set(request.measurementTimes)];
9240
+ if (times.length === 0 || times.some((time) => !Number.isFinite(time) || time > current.evaluatedAt))
9241
+ throw new POIError("Historical verification needs finite, non-future original measurement times");
9242
+ const versions = evidence.versions.map((version) => ({ ...version, parsed: ChargingStationProof.parse(version.proof) }));
9243
+ const used = /* @__PURE__ */ new Map();
9244
+ const bindings = [];
9245
+ for (const time of times) {
9246
+ const matches = [];
9247
+ const errors = [];
9248
+ for (const version2 of versions) {
9249
+ try {
9250
+ const operator = version2.parsed.roamingNetwork["operator"];
9251
+ const context = historicalOperatorContext(current, operator ?? null, time);
9252
+ const binding2 = verifyMeterKey(context, version2.proof, request);
9253
+ const verified = version2.parsed.verify(context);
9254
+ const operatorJSON = requireObject(operator, "The historical operator");
9255
+ const trust = OperatorTrust.fromOperatorJSON(context, operatorJSON);
9256
+ const holders = /* @__PURE__ */ new Set();
9257
+ for (const attestation of verified.attestations) {
9258
+ const certificate = trust.operator.signingKeyCertificates.find((item) => item.signingKey.id === attestation.keyId);
9259
+ if (certificate !== void 0 && !revoked(attestation.keyId, attestation.signedAt) && !revoked(certificate.keyId, attestation.signedAt)) {
9260
+ const holder = trust.holderOf(attestation);
9261
+ if (holder !== void 0) holders.add(holder);
9262
+ }
9263
+ }
9264
+ if (holders.size < context.requiredConsensus || revoked(binding2.key.id, time) || revoked(binding2.key.canonicalId, time))
9265
+ throw new POIError("Historical keys are affected by a caller-authorized compromise");
9266
+ matches.push({ binding: binding2, version: version2 });
9267
+ } catch (error) {
9268
+ errors.push(error instanceof Error ? error.message : String(error));
9269
+ }
9270
+ }
9271
+ if (matches.length === 0) throw new POIError(`No historical EVSE/meter/key coverage at ${new Date(time).toISOString()}: ${errors.join("; ")}`);
9272
+ const identities = new Set(matches.map(({ binding: binding2 }) => `${binding2.meterId}:${binding2.key.canonicalId}`));
9273
+ if (identities.size !== 1) throw new POIError("Conflicting historical key assignments cover the same measurement time");
9274
+ const selected = matches.sort((a, b) => a.binding.evidence.commitId.localeCompare(b.binding.evidence.commitId))[0];
9275
+ if (selected === void 0) throw new POIError("Missing historical selection");
9276
+ const { binding, version } = selected;
9277
+ used.set(binding.evidence.commitId, version);
9278
+ bindings.push(Object.freeze({ ...binding, evidence: Object.freeze({
9279
+ ...binding.evidence,
9280
+ verifiedAt: current.evaluatedAt,
9281
+ validUntil: current.validUntil,
9282
+ keyStatementDigest: current.keyStatementDigest,
9283
+ historicalStatementDigest: binding.evidence.keyStatementDigest,
9284
+ attestationValidUntil: binding.evidence.validUntil,
9285
+ measurementTimes: Object.freeze([time]),
9286
+ association: "historical-attestation"
9287
+ }) }));
9288
+ }
9289
+ const heads = /* @__PURE__ */ new Map();
9290
+ const publication = [];
9291
+ for (const [commitId, version] of used) {
9292
+ if (version.commitProof === void 0) {
9293
+ if (version.checkpoints.length > 0 || policy.requirePublication === true || requiredWitnesses > 0)
9294
+ throw new POIError("Required historical commit inclusion evidence is missing");
9295
+ publication.push({ commitId, publication: "missing", witnessedBy: [] });
9296
+ continue;
9297
+ }
9298
+ const proof = CheckpointProof.parse(version.commitProof);
9299
+ const entries = version.checkpoints.map((entry2) => ({ ...entry2, parsed: Checkpoint.parse(entry2.checkpoint) }));
9300
+ const entry = entries.find((item) => item.parsed.id === proof.checkpointId);
9301
+ if (entry === void 0) throw new POIError("The checkpoint of the historical commit proof is missing");
9302
+ const checkpoint = entry.parsed;
9303
+ if (checkpoint.roamingNetworkId !== current.roamingNetworkId || checkpoint.createdAt > current.evaluatedAt)
9304
+ throw new POIError("Foreign or future historical checkpoint");
9305
+ const coordinate2 = `${checkpoint.epoch}:${checkpoint.sequence}`;
9306
+ if (heads.has(coordinate2) && heads.get(coordinate2) !== checkpoint.id) throw new POIError("Conflicting historical checkpoint heads");
9307
+ heads.set(coordinate2, checkpoint.id);
9308
+ const checked = verifyAuthorizedCheckpoint(current, checkpoint, entry.operator, policy);
9309
+ const witnesses = checked.witnessedBy;
9310
+ proof.verify(checkpoint);
9311
+ version.parsed.verifyAgainstCommitProof(proof);
9312
+ publication.push({
9313
+ commitId,
9314
+ publication: witnesses.length > 0 ? "witnessed-checkpoint" : "rno-checkpoint",
9315
+ checkpointId: checkpoint.id,
9316
+ witnessedBy: [...witnesses]
9317
+ });
9318
+ }
9319
+ return Object.freeze({
9320
+ bindings: Object.freeze(bindings),
9321
+ versions: Object.freeze(publication.map((item) => Object.freeze(item))),
9322
+ checkedAt: current.evaluatedAt,
9323
+ timeBasis: "signed-measurement-timestamps",
9324
+ independentTime: "not-proven",
9325
+ latestVersion: "not-proven"
9326
+ });
9327
+ }
9328
+ function verifyAuthorizedCheckpoint(current, checkpoint, operator, policy = {}) {
9329
+ if (!(current instanceof TrustContext) && !(current instanceof HistoricalTrustContext)) throw new POIError("Checkpoint verification requires current local authority");
9330
+ for (const item of policy.revocations ?? []) if (!Number.isFinite(item.invalidFrom)) throw new POIError("Invalid checkpoint compromise time");
9331
+ const requiredWitnesses = policy.requiredWitnesses ?? 0;
9332
+ if (!Number.isSafeInteger(requiredWitnesses) || requiredWitnesses < 0) throw new POIError("Invalid checkpoint witness policy");
9333
+ if (checkpoint.roamingNetworkId !== current.roamingNetworkId || checkpoint.createdAt > current.evaluatedAt) throw new POIError("Foreign or future checkpoint");
9334
+ const revoked = (id, at) => (policy.revocations ?? []).some((item) => item.keyId === id && at >= item.invalidFrom);
9335
+ const profile = current.epochs.find((epoch) => epoch.number === checkpoint.epoch)?.profiles;
9336
+ if (profile === void 0) throw new POIError("The checkpoint epoch has no verified transition from the local policy");
9337
+ const holders = /* @__PURE__ */ new Set();
9338
+ const witnesses = /* @__PURE__ */ new Set();
9339
+ const witnessKeys = /* @__PURE__ */ new Set();
9340
+ let required2 = current instanceof HistoricalTrustContext ? current.authorityAt(checkpoint.createdAt)?.requiredConsensus ?? current.requiredConsensus : current.requiredConsensus;
9341
+ for (const signature of checkpoint.signatures) {
9342
+ if (!profile.allowsSignatureAlgorithm(signature.algorithm)) continue;
9343
+ if (signature.signedAt < checkpoint.createdAt || signature.signedAt > current.evaluatedAt || revoked(signature.keyId, signature.signedAt)) continue;
9344
+ try {
9345
+ if (signature.role === "roamingNetworkOperator") {
9346
+ const context = historicalOperatorContext(current, operator, signature.signedAt);
9347
+ required2 = Math.max(required2, context.requiredConsensus);
9348
+ const trust = OperatorTrust.fromOperatorJSON(context, operator);
9349
+ const certificate = trust.operator.signingKeyCertificates.find((item) => item.signingKey.id === signature.keyId);
9350
+ const key2 = trust.signingKeyOf(signature);
9351
+ const holder = trust.holderOf(signature);
9352
+ if (certificate === void 0 || revoked(certificate.keyId, signature.signedAt) || key2 === void 0 || holder === void 0) continue;
9353
+ checkpoint.verifySignature(signature, key2);
9354
+ holders.add(holder);
9355
+ } else if (signature.role === "witness") {
9356
+ const witness = policy.witnesses?.find((item) => item.witness === signature.signer && item.key.id === signature.keyId);
9357
+ if (witness === void 0 || !witness.key.isValidAt(signature.signedAt) || !witness.key.maySign("party") || witnessKeys.has(witness.key.id)) continue;
9358
+ checkpoint.verifySignature(signature, witness.key);
9359
+ witnesses.add(witness.witness);
9360
+ witnessKeys.add(witness.key.id);
9361
+ }
9362
+ } catch {
9363
+ }
9364
+ }
9365
+ if (holders.size < required2 || witnesses.size < requiredWitnesses) throw new POIError("Historical checkpoint lacks the locally required RNO/witness quorum");
9366
+ return { checkpoint, signedBy: [...holders], witnessedBy: [...witnesses] };
9367
+ }
9368
+
9369
+ // src/verifier/pricePublication.ts
9370
+ var PRICE_PUBLICATION_RECEIPT_CONTEXT = "https://open.charging.cloud/contexts/wwcp/pricePublicationReceipt/v1";
9371
+ var WITNESS_PUBLICATION_POLICY_CONTEXT = "https://open.charging.cloud/contexts/wwcp/witnessPublicationPolicy/v1";
9372
+ function pricePublicationOfferHashes(statement, tariff) {
9373
+ return { priceStatementSHA256: toHex(hash("sha256", canonicalJSONBytes(statement.json))), tariffSHA256: toHex(hash("sha256", canonicalJSONBytes(tariff))) };
9374
+ }
9375
+ function identifier(json, property) {
9376
+ const text = requireString(json, property);
9377
+ if (text.trim() !== text || text.length === 0 || text.length > 512) throw new POIError("Invalid publication identity");
9378
+ for (let index = 0; index < text.length; index++) if (text.charCodeAt(index) < 32) throw new POIError("Invalid publication identity");
9379
+ return text;
9380
+ }
9381
+ function publicPublicationURL(text) {
9382
+ const url = new URL(text);
9383
+ if (url.protocol !== "https:" || url.username !== "" || url.password !== "" || url.hash !== "" || text.length > 2048) throw new POIError("Publication sources require public HTTPS without credentials or fragments");
9384
+ const host = url.hostname.toLowerCase().replace(/^\[|\]$/gu, "");
9385
+ if (host === "localhost" || host.endsWith(".localhost") || host.endsWith(".local") || host === "::1" || host.includes(":") || /^\d+(\.\d+){3}$/u.test(host) || !host.includes(".")) throw new POIError("Publication sources require a public DNS hostname");
9386
+ return url.href;
9387
+ }
9388
+ function parseWitnessPublicationPolicy(value) {
9389
+ const json = requireObject(value, "Witness publication policy");
9390
+ onlyProperties(json, ["@context", "requiredOrganizations", "witnesses"], "Witness publication policy");
9391
+ if (json["@context"] !== WITNESS_PUBLICATION_POLICY_CONTEXT) throw new POIError("Unsupported witness publication policy");
9392
+ const requiredOrganizations = requireInteger(json, "requiredOrganizations", 1, 16);
9393
+ const entries = requireArray(json["witnesses"], "Publication witnesses");
9394
+ if (entries.length > 32 || canonicalJSONBytes(json).length > 1024 * 1024) throw new POIError("Witness policy exceeds its capacity");
9395
+ const witnesses = [], ids = /* @__PURE__ */ new Set(), material = /* @__PURE__ */ new Map();
9396
+ for (const entry of entries) {
9397
+ const item = requireObject(entry, "A publication witness");
9398
+ onlyProperties(item, ["id", "organization", "role", "networks", "keys", "notBefore", "notAfter", "clockErrorMs", "endpoints"], "A publication witness");
9399
+ if (item["role"] !== "price-publication-and-time") throw new POIError("A witness needs the explicit publication and time role");
9400
+ const id = identifier(item, "id"), organization = identifier(item, "organization");
9401
+ if (ids.has(id)) throw new POIError("Duplicate witness identity");
9402
+ ids.add(id);
9403
+ const networks = requireArray(item["networks"], "Witness network scopes");
9404
+ if (networks.length === 0 || networks.length > 32 || networks.some((value2) => typeof value2 !== "string" || value2.length === 0 || value2 === "*") || new Set(networks).size !== networks.length) throw new POIError("Explicit witness network scopes required");
9405
+ const keys = POIPublicKey.parseAll(item["keys"], "Witness publication keys");
9406
+ if (keys.length === 0 || keys.length > 8 || new Set(keys.map((key2) => key2.id)).size !== keys.length) throw new POIError("Invalid witness key list");
9407
+ for (const key2 of keys) {
9408
+ if (!key2.maySign("witnessPublications")) throw new POIError("Witness key usage does not permit publication receipts");
9409
+ const fingerprint = toHex(key2.key.thumbprint()), before = material.get(fingerprint);
9410
+ if (before !== void 0 && before !== organization) throw new POIError("The same witness key cannot represent different organizations");
9411
+ material.set(fingerprint, organization);
9412
+ }
9413
+ const notBefore = requireTimestamp(item, "notBefore"), notAfter = requireTimestamp(item, "notAfter");
9414
+ if (notAfter <= notBefore) throw new POIError("Invalid witness validity interval");
9415
+ const clockErrorMs = requireInteger(item, "clockErrorMs", 0, 3e5);
9416
+ const endpoints = requireArray(item["endpoints"], "Witness receipt endpoints");
9417
+ if (endpoints.length > 4 || endpoints.some((url) => typeof url !== "string")) throw new POIError("Invalid witness receipt endpoints");
9418
+ witnesses.push(Object.freeze({
9419
+ id,
9420
+ organization,
9421
+ networks: Object.freeze(networks),
9422
+ keys: Object.freeze(keys),
9423
+ notBefore,
9424
+ notAfter,
9425
+ clockErrorMs,
9426
+ endpoints: Object.freeze(endpoints.map(publicPublicationURL)),
9427
+ json: item
9428
+ }));
9429
+ }
9430
+ return Object.freeze({ requiredOrganizations, witnesses: Object.freeze(witnesses), json });
9431
+ }
9432
+ var PricePublicationReceipt = class _PricePublicationReceipt {
9433
+ constructor(json, observedAt, signedAt) {
9434
+ this.json = json;
9435
+ this.observedAt = observedAt;
9436
+ this.signedAt = signedAt;
9437
+ }
9438
+ json;
9439
+ observedAt;
9440
+ signedAt;
9441
+ static parse(value) {
9442
+ const json = requireObject(value, "Price publication receipt");
9443
+ onlyProperties(json, ["@context", "assertion", "roamingNetworkId", "EVSEId", "priceStatementDigest", "priceStatementSHA256", "tariffSHA256", "checkpointId", "witnessId", "publicSourceURL", "observedAt", "signedAt", "algorithm", "keyId", "value"], "Price publication receipt");
9444
+ if (json["@context"] !== PRICE_PUBLICATION_RECEIPT_CONTEXT || json["assertion"] !== "public-price-retrieval") throw new POIError("Unsupported publication receipt assertion");
9445
+ for (const field of ["roamingNetworkId", "EVSEId", "witnessId", "algorithm", "keyId"]) identifier(json, field);
9446
+ const signature = requireString(json, "value");
9447
+ if (signature.length === 0 || signature.length > 131072) throw new POIError("Invalid publication signature size");
9448
+ if (!isLowerHex(requireString(json, "priceStatementDigest"), 32) || !/^(?:[0-9a-f]{64}|[0-9a-f]{96}|[0-9a-f]{128})$/u.test(requireString(json, "checkpointId"))) throw new POIError("Invalid publication receipt digest");
9449
+ for (const field of ["priceStatementSHA256", "tariffSHA256"]) if (!isLowerHex(requireString(json, field), 32)) throw new POIError("Invalid signed-price or tariff digest");
9450
+ publicPublicationURL(requireString(json, "publicSourceURL"));
9451
+ const observedAt = requireTimestamp(json, "observedAt"), signedAt = requireTimestamp(json, "signedAt");
9452
+ if (signedAt < observedAt || signedAt - observedAt > 6e4) throw new POIError("A publication receipt must be signed within one minute after observation");
9453
+ return new _PricePublicationReceipt(json, observedAt, signedAt);
9454
+ }
9455
+ signingBytes() {
9456
+ const { value: _, ...signed } = this.json;
9457
+ return canonicalJSONBytes(signed);
9458
+ }
9459
+ digest() {
9460
+ return toHex(hash("sha256", canonicalJSONBytes(this.json)));
9461
+ }
9462
+ };
9463
+ function verifyPricePublication(receipts, policy, subject) {
9464
+ if (!Number.isFinite(subject.chargingStarted) || !Number.isFinite(subject.now) || subject.chargingStarted > subject.now || receipts.length > 64) throw new POIError("Invalid publication evaluation context");
9465
+ const accepted = policy === void 0 ? void 0 : parseWitnessPublicationPolicy(policy.json);
9466
+ const organizations = /* @__PURE__ */ new Set(), rows = [];
9467
+ for (const raw of receipts) {
9468
+ let receipt;
9469
+ try {
9470
+ receipt = PricePublicationReceipt.parse(raw);
9471
+ const json = receipt.json, witness = accepted?.witnesses.find((item) => item.id === json["witnessId"]);
9472
+ if (witness?.networks.includes(subject.roamingNetworkId) !== true) {
9473
+ rows.push({ witnessId: requireString(json, "witnessId"), status: "untrusted", detail: "No locally authorized witness for this network" });
9474
+ continue;
9475
+ }
9476
+ const key2 = witness.keys.find((item) => item.id === json["keyId"]);
9477
+ if (key2 === void 0) throw new POIError("The receipt key is not authorized for this witness");
9478
+ if ((subject.excludedIdentities ?? []).includes(witness.id) || (subject.excludedIdentities ?? []).includes(witness.organization) || (subject.excludedKeyMaterial ?? []).includes(toHex(key2.key.thumbprint()))) throw new POIError("An RNO/CPO authority cannot count as an independent publication witness");
9479
+ const lower = receipt.observedAt - witness.clockErrorMs, upper = receipt.signedAt + witness.clockErrorMs;
9480
+ if (lower < witness.notBefore || upper >= witness.notAfter || !key2.isValidAt(lower) || !key2.isValidAt(upper)) throw new POIError("Witness/key does not cover the observation and signing interval");
9481
+ if (receipt.signedAt > subject.now || json["roamingNetworkId"] !== subject.roamingNetworkId || json["EVSEId"] !== subject.evseId || json["priceStatementDigest"] !== subject.statement.digestHex()) throw new POIError("Foreign, substituted or future publication receipt");
9482
+ const hashes = pricePublicationOfferHashes(subject.statement, subject.tariff);
9483
+ if (json["priceStatementSHA256"] !== hashes.priceStatementSHA256 || json["tariffSHA256"] !== hashes.tariffSHA256) throw new POIError("Receipt does not bind the exact original CPO signature and selected tariff definition/currency");
9484
+ const checkpoint = subject.checkpoints.find((item) => item.id === json["checkpointId"]);
9485
+ if (checkpoint === void 0 || checkpoint.createdAt > receipt.observedAt + witness.clockErrorMs || subject.statement.signedAt > receipt.observedAt + witness.clockErrorMs) throw new POIError("Receipt needs the exact authorized price checkpoint and a consistent observation time");
9486
+ verifySignature(requireString(json, "algorithm"), key2.key, receipt.signingBytes(), requireString(json, "value"));
9487
+ const common = { witnessId: witness.id, organization: witness.organization, keyId: key2.id, receiptDigest: receipt.digest(), latestPublicationAt: upper, sourceURL: requireString(json, "publicSourceURL") };
9488
+ if (upper >= subject.chargingStarted) rows.push({ ...common, status: "after-start", detail: "The latest observation/signing time including the accepted clock error is not before the signed start" });
9489
+ else if (organizations.has(witness.organization)) rows.push({ ...common, status: "duplicate-organization", detail: "Only one vote per independently approved organization" });
9490
+ else {
9491
+ organizations.add(witness.organization);
9492
+ rows.push({ ...common, status: "counted", detail: "Public retrieval of this exact included offer witnessed before its signed charging start" });
9493
+ }
9494
+ } catch (error) {
9495
+ rows.push({ witnessId: typeof receipt?.json["witnessId"] === "string" ? receipt.json["witnessId"] : "", status: "invalid", detail: error instanceof Error ? error.message : String(error) });
9496
+ }
9497
+ }
9498
+ const requiredOrganizations = accepted?.requiredOrganizations ?? 2;
9499
+ return Object.freeze({
9500
+ status: organizations.size >= requiredOrganizations ? "witnessed-before-signed-start" : rows.some((row) => row.status === "invalid") ? "invalid" : "not-proven",
9501
+ requiredOrganizations,
9502
+ organizations: Object.freeze([...organizations].sort()),
9503
+ timeBasis: "trusted-witness-clocks-versus-signed-start",
9504
+ actualStartTime: "not-independently-proven",
9505
+ receipts: Object.freeze(rows)
9506
+ });
9507
+ }
9508
+
9509
+ // src/verifier/sessionPrices.ts
9510
+ var SESSION_PRICE_EVIDENCE_CONTEXT = "https://open.charging.cloud/contexts/wwcp/sessionPriceEvidence/v1";
9511
+ var SESSION_PRICE_AGREEMENT_CONTEXT = "https://open.charging.cloud/contexts/wwcp/sessionPriceAgreement/v1";
9512
+ var SIMPLE_GROSS_PRICE_PROFILE = "chargy-gross-rates/v1";
9513
+ var SessionPriceAgreement = class _SessionPriceAgreement {
9514
+ constructor(json, chargingStarted, signedAt) {
9515
+ this.json = json;
9516
+ this.chargingStarted = chargingStarted;
9517
+ this.signedAt = signedAt;
9518
+ }
9519
+ json;
9520
+ chargingStarted;
9521
+ signedAt;
9522
+ static parse(value) {
9523
+ const json = requireObject(value, "A session price assignment");
9524
+ onlyProperties(json, ["@context", "roamingNetworkId", "EVSEId", "meterSerialNumber", "chargingStarted", "startDocumentSHA256", "chargingMode", "chargingTariffId", "priceStatementDigest", "priceBinding", "pricingProfile", "role", "signer", "signedAt", "algorithm", "keyId", "value"], "A session price assignment");
9525
+ if (json["@context"] !== SESSION_PRICE_AGREEMENT_CONTEXT || json["role"] !== "chargingStationOperator") throw new POIError("Unsupported price assignment context/role");
9526
+ for (const field of ["roamingNetworkId", "EVSEId", "meterSerialNumber", "chargingMode", "chargingTariffId", "priceBinding", "pricingProfile", "signer", "algorithm", "keyId", "value"]) requireString(json, field);
9527
+ for (const field of ["startDocumentSHA256", "priceStatementDigest"]) if (!isLowerHex(requireString(json, field), 32)) throw new POIError("Invalid price assignment digest");
9528
+ return new _SessionPriceAgreement(json, requireTimestamp(json, "chargingStarted"), requireTimestamp(json, "signedAt"));
9529
+ }
9530
+ signingBytes() {
9531
+ const { value: _, ...signed } = this.json;
9532
+ return canonicalJSONBytes(signed);
9533
+ }
9534
+ verify(station) {
9535
+ if (this.json["roamingNetworkId"] !== station.roamingNetwork.id || this.json["signer"] !== station.operator["@id"]) throw new POIError("Foreign price assignment authority");
9536
+ const key2 = station.operatorSigningKeys.find((item) => item.id === this.json["keyId"]);
9537
+ if (key2 === void 0 || !key2.isValidAt(this.signedAt) || !key2.isValidAt(this.chargingStarted) || !key2.maySign("priceStatements")) throw new POIError("Unauthorized/expired price assignment key");
9538
+ verifySignature(requireString(this.json, "algorithm"), key2.key, this.signingBytes(), requireString(this.json, "value"));
9539
+ }
9540
+ };
9541
+ function parseSessionPriceEvidence(value) {
9542
+ const json = requireObject(value, "Session price evidence");
9543
+ onlyProperties(json, ["@context", "EVSEId", "chargingStationProof", "priceStatements", "agreement", "priceStatementProofs", "checkpoints", "publicationReceipts", "witnessCandidates"], "Session price evidence");
9544
+ if (json["@context"] !== SESSION_PRICE_EVIDENCE_CONTEXT) throw new POIError("Unsupported session price evidence");
9545
+ const publicationReceipts = json["publicationReceipts"] === void 0 ? [] : requireArray(json["publicationReceipts"], "Publication receipts");
9546
+ const witnessCandidates = json["witnessCandidates"] === void 0 ? [] : requireArray(json["witnessCandidates"], "Untrusted witness recommendations");
9547
+ if (publicationReceipts.length > 64 || witnessCandidates.length > 32) throw new POIError("Too many publication receipts or recommendations");
9548
+ return {
9549
+ publicationReceipts,
9550
+ witnessCandidates,
9551
+ evseId: requireString(json, "EVSEId"),
9552
+ proof: requireObject(json["chargingStationProof"], "The priced station proof"),
9553
+ priceStatements: requireArray(json["priceStatements"], "Price statements"),
9554
+ ...json["agreement"] === void 0 ? {} : { agreement: json["agreement"] },
9555
+ priceProofs: json["priceStatementProofs"] === void 0 ? [] : requireArray(json["priceStatementProofs"], "Price inclusion proofs"),
9556
+ checkpoints: (json["checkpoints"] === void 0 ? [] : requireArray(json["checkpoints"], "Price checkpoints")).map((item) => {
9557
+ const entry = requireObject(item, "A price checkpoint entry");
9558
+ onlyProperties(entry, ["checkpoint", "operator"], "A price checkpoint entry");
9559
+ return { checkpoint: requireObject(entry["checkpoint"], "A price checkpoint"), operator: requireObject(entry["operator"], "The checkpoint authority") };
9560
+ })
9561
+ };
9562
+ }
9563
+ function grossRates(currency, elements) {
9564
+ if (elements.length !== 1) return void 0;
9565
+ const element = elements[0];
9566
+ if (element === void 0 || Object.keys(element).some((key2) => key2 !== "priceComponents" && key2 !== "restrictions")) return void 0;
9567
+ if (element["restrictions"] !== void 0 && (!isObject(element["restrictions"]) || Object.keys(element["restrictions"]).length > 0)) return void 0;
9568
+ const components = element["priceComponents"];
9569
+ if (!Array.isArray(components) || components.length === 0) return void 0;
9570
+ const rates = { currency };
9571
+ for (const raw of components) {
9572
+ if (!isObject(raw) || Object.keys(raw).some((key2) => !["type", "price", "stepSize"].includes(key2))) return void 0;
9573
+ const type = raw["type"], value = raw["price"], step = raw["stepSize"];
9574
+ if (!isNumber(value) || Decimal.parse(asText(value)).mantissa < 0n || typeof step !== "number" || !Number.isSafeInteger(step) || step < 1) return void 0;
9575
+ const field = type === "ENERGY" ? "energyPerKWh" : type === "FLAT" ? "flatFee" : type === "TIME" ? "timePerHour" : void 0;
9576
+ if (field === void 0 || rates[field] !== void 0) return void 0;
9577
+ rates[field] = asText(value);
9578
+ }
9579
+ return rates;
9580
+ }
9581
+ function verifySessionPriceEvidence(current, value, request, policy = {}) {
9582
+ if (!(current instanceof TrustContext) && !(current instanceof HistoricalTrustContext)) throw new POIError("Session prices require current local authorization");
9583
+ if (!Number.isFinite(request.chargingStarted) || request.chargingStarted > current.evaluatedAt || !isLowerHex(request.startDocumentSHA256, 32)) throw new POIError("Invalid original price context");
9584
+ for (const item of policy.revocations ?? []) if (!Number.isFinite(item.invalidFrom)) throw new POIError("Invalid compromise time");
9585
+ const evidence = parseSessionPriceEvidence(value);
9586
+ if (evidence.evseId !== request.evseId) throw new POIError("Price evidence belongs to another EVSE");
9587
+ const proof = ChargingStationProof.parse(evidence.proof), context = historicalOperatorContext(current, proof.roamingNetwork["operator"] ?? null, request.chargingStarted);
9588
+ const station = verifyChargingStation(context, evidence.proof, { evseId: request.evseId }), diagnostics = [];
9589
+ const base = { station, publicationBeforeStart: "not-proven", customerConsent: "not-proven", latestOrSoleOffer: "not-proven", checkedAt: current.evaluatedAt, checkpointIds: [], witnessedBy: [] };
9590
+ const fail = (signatures, applicability2, message) => ({ ...base, signatures, applicability: applicability2, agreement: "missing", publication: "missing", chainGaps: false, diagnostics: [message] });
9591
+ if (evidence.priceStatements.length === 0) return fail("missing", "missing", "No signed price statement supplied");
9592
+ let statements;
9593
+ try {
9594
+ statements = evidence.priceStatements.map((item) => PriceStatement.parse(item));
9595
+ for (const statement of statements) {
9596
+ statement.verify(station.roamingNetwork.id, asText(station.operator["@id"]), station.operatorSigningKeys);
9597
+ if (statement.signedAt > current.evaluatedAt || (policy.revocations ?? []).some((item) => item.keyId === statement.keyId && statement.signedAt >= item.invalidFrom)) throw new POIError("Future/revoked CPO price signature");
9598
+ }
9599
+ } catch (error) {
9600
+ return fail("invalid", "invalid", error instanceof Error ? error.message : String(error));
9601
+ }
9602
+ let active;
9603
+ try {
9604
+ active = verifyPriceStatements(station, [...evidence.priceStatements], { at: request.chargingStarted });
9605
+ } catch (error) {
9606
+ return fail("valid", "invalid", error instanceof Error ? error.message : String(error));
9607
+ }
9608
+ const chainGaps = statements.some((statement) => statement.sequence > 0 && !statements.some((before) => statement.follows(before)));
9609
+ let agreement;
9610
+ if (evidence.agreement !== void 0) {
9611
+ try {
9612
+ agreement = SessionPriceAgreement.parse(evidence.agreement);
9613
+ agreement.verify(station);
9614
+ const a = agreement.json;
9615
+ if (a["EVSEId"] !== request.evseId || a["meterSerialNumber"] !== request.meterSerialNumber || agreement.chargingStarted !== request.chargingStarted || a["startDocumentSHA256"] !== request.startDocumentSHA256 || agreement.signedAt > current.evaluatedAt || (policy.revocations ?? []).some((item) => item.keyId === a["keyId"] && agreement !== void 0 && agreement.signedAt >= item.invalidFrom)) throw new POIError("Price assignment does not match this original session");
9616
+ } catch (error) {
9617
+ return { ...fail("valid", "invalid", error instanceof Error ? error.message : String(error)), agreement: "invalid", chainGaps };
9618
+ }
9619
+ }
9620
+ const tariffs = station.tariffs.get(request.evseId) ?? [];
9621
+ const candidates = [...active.values()].flatMap(({ statement, digest: digest2 }) => statement.prices.flatMap((price) => {
9622
+ const tariff = tariffs.find((item) => item["@id"] === price["chargingTariffId"]);
9623
+ return tariff === void 0 ? [] : [{ statement, digest: digest2, tariff, price }];
9624
+ }));
9625
+ const eligible = agreement === void 0 ? candidates : candidates.filter((item) => item.digest === agreement.json["priceStatementDigest"] && item.tariff["@id"] === agreement.json["chargingTariffId"]);
9626
+ if (eligible.length !== 1) return { ...fail("valid", eligible.length === 0 ? "missing" : "ambiguous", eligible.length === 0 ? "No selected tariff/price is active at charging start" : "Competing price/tariff candidates need a signed selection"), agreement: agreement === void 0 ? "missing" : "invalid", chainGaps };
9627
+ const selected = eligible[0];
9628
+ if (selected === void 0) throw new POIError("Missing selected price");
9629
+ const currency = requireString(selected.tariff, "currency"), elements = requireArray(selected.price["elements"], "Signed price elements").map((item) => requireObject(item, "A signed price element")), rates = grossRates(currency, elements);
9630
+ let applicability = agreement === void 0 ? "unbound" : "bound";
9631
+ if (agreement !== void 0 && (agreement.json["chargingMode"] !== "adhoc" || agreement.json["priceBinding"] !== "fixed-at-start" || agreement.json["pricingProfile"] !== SIMPLE_GROSS_PRICE_PROFILE || rates === void 0)) {
9632
+ applicability = "unsupported";
9633
+ diagnostics.push("EMP/contract terms, variable binding, unknown price profile or conditional elements are not established by this profile");
9634
+ }
9635
+ let publication = "missing";
9636
+ const checkpointIds = [], witnessed = /* @__PURE__ */ new Set();
9637
+ if (evidence.priceProofs.length > 0 || evidence.checkpoints.length > 0) {
9638
+ try {
9639
+ const entries = evidence.checkpoints.map((entry) => ({ ...entry, checked: verifyAuthorizedCheckpoint(current, Checkpoint.parse(entry.checkpoint), entry.operator, policy) })), byId3 = new Map(entries.map((entry) => [entry.checked.checkpoint.id, entry.checked])), heads = /* @__PURE__ */ new Map();
9640
+ for (const checked of [...entries.map((entry) => entry.checked), ...(policy.relatedCheckpoints ?? []).map((entry) => verifyAuthorizedCheckpoint(current, Checkpoint.parse(entry.checkpoint), entry.operator, policy))]) {
9641
+ const cp = checked.checkpoint, coordinate2 = `${cp.epoch}:${cp.sequence}`;
9642
+ if (heads.has(coordinate2) && heads.get(coordinate2) !== cp.id) throw new POIError("Conflicting station/price checkpoint heads");
9643
+ heads.set(coordinate2, cp.id);
9644
+ }
9645
+ let selectedProof = false;
9646
+ for (const raw of evidence.priceProofs) {
9647
+ const p = PriceStatementProof.parse(raw), checked = byId3.get(p.checkpointId);
9648
+ if (checked === void 0) throw new POIError("Missing price checkpoint");
9649
+ p.priceStatement.verify(station.roamingNetwork.id, asText(station.operator["@id"]), station.operatorSigningKeys);
9650
+ if (!statements.some((statement) => statement.digestHex() === p.priceStatement.digestHex())) throw new POIError("Price proof belongs to an unsupplied statement");
9651
+ p.verify(checked.checkpoint);
9652
+ if (p.priceStatement.digestHex() !== selected.digest) continue;
9653
+ selectedProof = true;
9654
+ checkpointIds.push(checked.checkpoint.id);
9655
+ for (const name of checked.witnessedBy) witnessed.add(name);
9656
+ if (p.priceStatement.after !== void 0) {
9657
+ const after = byId3.get(p.priceStatement.after)?.checkpoint;
9658
+ if (after === void 0) {
9659
+ publication = "incomplete";
9660
+ diagnostics.push("Missing checkpoint named by after-link");
9661
+ continue;
9662
+ }
9663
+ if (after.epoch !== checked.checkpoint.epoch || after.sequence >= checked.checkpoint.sequence) throw new POIError("Invalid checkpoint temporal order");
9664
+ verifyCheckpointLine(entries.map((entry) => entry.checked.checkpoint).filter((cp) => cp.epoch === after.epoch && cp.sequence >= after.sequence && cp.sequence <= checked.checkpoint.sequence).sort((a, b) => a.sequence - b.sequence));
9665
+ }
9666
+ }
9667
+ if (publication !== "incomplete") publication = selectedProof ? "valid" : "missing";
9668
+ } catch (error) {
9669
+ publication = "invalid";
9670
+ diagnostics.push(error instanceof Error ? error.message : String(error));
9671
+ }
9672
+ }
9673
+ const operator = proof.roamingNetwork["operator"];
9674
+ const operatorJSON = isObject(operator) ? operator : {};
9675
+ const authorities = [operatorJSON, ...evidence.checkpoints.map((entry) => isObject(entry.operator) ? entry.operator : {})];
9676
+ const partyKeys = [...station.operatorSigningKeys, ...POIPublicKey.parseAll(station.operator["identityKeys"] ?? [], "CPO identity keys")];
9677
+ for (const authority of authorities) {
9678
+ partyKeys.push(...POIPublicKey.parseAll(authority["identityKeys"] ?? [], "RNO identity keys"));
9679
+ const certificates = Array.isArray(authority["signingKeyCertificates"]) ? authority["signingKeyCertificates"] : [];
9680
+ for (const certificate of certificates) if (isObject(certificate) && certificate["signingKey"] !== void 0) partyKeys.push(POIPublicKey.parse(certificate["signingKey"]));
9681
+ }
9682
+ const publicationWitnesses = verifyPricePublication(evidence.publicationReceipts, policy.publicationPolicy, {
9683
+ roamingNetworkId: station.roamingNetwork.id,
9684
+ evseId: request.evseId,
9685
+ statement: selected.statement,
9686
+ tariff: selected.tariff,
9687
+ chargingStarted: request.chargingStarted,
9688
+ now: current.evaluatedAt,
9689
+ checkpoints: publication === "valid" ? evidence.checkpoints.map((entry) => Checkpoint.parse(entry.checkpoint)).filter((cp) => checkpointIds.includes(cp.id)) : [],
9690
+ excludedKeyMaterial: partyKeys.map((key2) => toHex(key2.key.thumbprint())),
9691
+ excludedIdentities: [asText(station.operator["@id"]), ...authorities.map((authority) => asText(authority["@id"]))]
9692
+ });
9693
+ if (policy.requirePublication === true && publication !== "valid") diagnostics.push("Required price publication evidence is incomplete");
9694
+ return {
9695
+ ...base,
9696
+ publicationWitnesses,
9697
+ publicationBeforeStart: publicationWitnesses.status,
9698
+ signatures: "valid",
9699
+ applicability,
9700
+ agreement: agreement === void 0 ? "missing" : "valid",
9701
+ publication,
9702
+ chainGaps,
9703
+ diagnostics,
9704
+ checkpointIds,
9705
+ witnessedBy: [...witnessed],
9706
+ selected: { statement: selected.statement.json, digest: selected.digest, tariff: selected.tariff, currency, elements, ...rates === void 0 || agreement?.json["pricingProfile"] !== SIMPLE_GROSS_PRICE_PROFILE ? {} : { rates } }
9707
+ };
9708
+ }
9709
+
9710
+ // src/verifier/observedVersions.ts
9711
+ function emptyObservedVersions() {
9712
+ return { version: 1, points: [], heads: {}, conflicts: [] };
9713
+ }
9714
+ function observedVersionsChecksum(value) {
9715
+ return toHex(hash("sha256", canonicalJSONBytes(value)));
9716
+ }
9717
+ function key(point) {
9718
+ return JSON.stringify([point.kind, point.chain]);
9719
+ }
9720
+ function coordinate(point) {
9721
+ return JSON.stringify([point.kind, point.chain, point.epoch, point.position]);
9722
+ }
9723
+ function digest(value) {
9724
+ return /^(?:[0-9a-f]{64}|[0-9a-f]{96}|[0-9a-f]{128})$/u.test(value);
9725
+ }
9726
+ function isSchema(value) {
9727
+ return value === 1;
9728
+ }
9729
+ function valid(point) {
9730
+ if (!["station", "checkpoint", "price", "live", "policy"].includes(point.kind) || point.chain.length === 0 || point.chain.length > 2048 || point.epoch.length > 128 || !Number.isSafeInteger(point.position) || point.position < 0 || !digest(point.digest) || point.previous !== void 0 && !digest(point.previous) || point.identity !== void 0 && !digest(point.identity) || point.revision !== void 0 && (!Number.isSafeInteger(point.revision) || point.revision < 0) || typeof point.original !== "string" || point.original.length === 0 || point.original.length > 4 * 1024 * 1024)
9731
+ throw new POIError("Invalid observed version");
9732
+ }
9733
+ function compareObservedVersions(before, candidates, mode, observedAt) {
9734
+ if (!isSchema(before.version) || !Number.isFinite(observedAt) || before.points.length > 4096 || before.conflicts.length > 128) throw new POIError("Invalid observed history");
9735
+ for (const point of [...before.points, ...candidates]) valid(point);
9736
+ const points = new Map(before.points.map((point) => [coordinate(point), point])), conflicts = [...before.conflicts], issues = [];
9737
+ const heads = { ...before.heads };
9738
+ const issue = (point, code, detail) => {
9739
+ issues.push({ code, kind: point.kind, chain: point.chain, detail });
9740
+ };
9741
+ const conflict = (point, known, code, detail) => {
9742
+ issue(point, code, detail);
9743
+ if (!conflicts.some((item) => item.code === code && item.kind === point.kind && item.chain === point.chain && item.epoch === point.epoch && item.known === known.digest && item.incoming === point.digest))
9744
+ conflicts.push({ code, kind: point.kind, chain: point.chain, epoch: point.epoch, position: point.position, known: known.digest, incoming: point.digest, original: point.original, observedAt });
9745
+ };
9746
+ for (const point of candidates) {
9747
+ const existing = points.get(coordinate(point));
9748
+ if (existing !== void 0 && existing.digest !== point.digest) {
9749
+ conflict(point, existing, "fork", "Two authenticated digests at the same coordinate");
9750
+ continue;
9751
+ }
9752
+ const identity = [...points.values()].find((item) => key(item) === key(point) && item.identity !== void 0 && point.identity !== void 0 && item.identity !== point.identity);
9753
+ if (identity !== void 0) {
9754
+ conflict(point, identity, "identity", "The identity or pinned document keys of an observed chain changed");
9755
+ continue;
9756
+ }
9757
+ if (point.kind === "live" && point.previous !== void 0) {
9758
+ const sibling = [...points.values()].find((item) => key(item) === key(point) && item.epoch === point.epoch && item.previous === point.previous && item.digest !== point.digest);
9759
+ if (sibling !== void 0) {
9760
+ conflict(point, sibling, "link", "Two authenticated live updates name the same predecessor");
9761
+ continue;
9762
+ }
9763
+ }
9764
+ if (existing === void 0) points.set(coordinate(point), point);
9765
+ }
9766
+ const groups = /* @__PURE__ */ new Map();
9767
+ for (const point of points.values()) {
9768
+ const group = JSON.stringify([point.kind, point.chain, point.epoch]);
9769
+ groups.set(group, [...groups.get(group) ?? [], point]);
9770
+ }
9771
+ for (const group of groups.values()) {
9772
+ if (!candidates.some((candidate) => group.some((point) => key(point) === key(candidate)))) continue;
9773
+ group.sort((a, b) => a.position - b.position);
9774
+ for (let i = 1; i < group.length; i++) {
9775
+ const point = group[i], previous = group[i - 1];
9776
+ if (point === void 0 || previous === void 0 || point.kind === "station") continue;
9777
+ if ((point.kind === "price" || point.kind === "checkpoint") && point.position === previous.position + 1 && point.previous !== previous.digest)
9778
+ conflict(point, previous, "link", "Authenticated adjacent versions contradict their predecessor link");
9779
+ if (point.kind === "checkpoint" && point.previous === previous.digest && point.revision !== void 0 && previous.revision !== void 0 && (point.revision < previous.revision || point.revision === previous.revision && !(point.final === true && point.rootDigest === previous.rootDigest))) conflict(point, previous, "link", "Checkpoint revision does not increase");
9780
+ }
9781
+ }
9782
+ const selected = /* @__PURE__ */ new Map();
9783
+ for (const point of candidates) {
9784
+ const name = key(point), old = selected.get(name);
9785
+ if (old === void 0 || old.epoch === point.epoch && point.position > old.position) selected.set(name, point);
9786
+ }
9787
+ for (const point of selected.values()) {
9788
+ if (conflicts.some((item) => item.kind === point.kind && item.chain === point.chain)) issue(point, "known-fork", "This chain has an unresolved authenticated contradiction");
9789
+ if (mode === "historical") continue;
9790
+ if (new Set(candidates.filter((candidate) => key(candidate) === key(point)).map((candidate) => candidate.epoch)).size > 1) {
9791
+ issue(point, "epoch-transition", "A current selection cannot combine epochs without a verified transition");
9792
+ continue;
9793
+ }
9794
+ const head = before.heads[key(point)];
9795
+ const closed = before.points.find((item) => item.kind === "checkpoint" && item.epoch === point.epoch && item.final === true);
9796
+ if (point.kind === "checkpoint" && closed !== void 0 && point.position > closed.position) {
9797
+ issue(point, "epoch-transition", "This epoch was closed by its final checkpoint");
9798
+ continue;
9799
+ }
9800
+ if (head !== void 0 && head.epoch !== point.epoch) {
9801
+ issue(point, "epoch-transition", "An epoch change requires an explicitly verified transition");
9802
+ continue;
9803
+ }
9804
+ const prior = before.points.filter((item) => key(item) === key(point) && item.epoch === point.epoch).sort((a, b) => b.position - a.position)[0];
9805
+ if (prior !== void 0 && point.position < prior.position) {
9806
+ issue(point, "rollback", `Version ${point.position} is older than locally observed ${prior.position}`);
9807
+ continue;
9808
+ }
9809
+ const anchor = head === void 0 ? prior : before.points.find((item) => key(item) === key(point) && item.epoch === head.epoch && item.digest === head.digest);
9810
+ if (anchor !== void 0 && point.position > anchor.position && point.kind !== "station") {
9811
+ let cursor = point;
9812
+ const visited = /* @__PURE__ */ new Set();
9813
+ while (cursor.digest !== anchor.digest && !visited.has(cursor.digest)) {
9814
+ visited.add(cursor.digest);
9815
+ const parent = [...points.values()].find((item) => key(item) === key(point) && item.epoch === point.epoch && item.digest === cursor?.previous);
9816
+ if (parent === void 0 || parent.position >= cursor.position || point.kind !== "live" && parent.position + 1 !== cursor.position) {
9817
+ cursor = void 0;
9818
+ break;
9819
+ }
9820
+ cursor = parent;
9821
+ }
9822
+ if (cursor?.digest !== anchor.digest) issue(point, "continuity-missing", "Missing authenticated predecessor path to the locally observed head");
9823
+ }
9824
+ }
9825
+ const accepted = issues.length === 0;
9826
+ if (accepted && mode === "current") for (const point of selected.values()) heads[key(point)] = { epoch: point.epoch, position: point.position, digest: point.digest };
9827
+ if (points.size > 4096 || conflicts.length > 128) throw new POIError("Observed history capacity reached; reset requires an explicit local decision");
9828
+ const state = { ...before, version: 1, points: [...points.values()], heads, conflicts };
9829
+ const advanced = candidates.some((point) => before.points.some((old) => key(old) === key(point) && old.epoch === point.epoch && old.position < point.position));
9830
+ return { accepted, status: !accepted ? "rejected" : mode === "historical" ? "historical" : !before.points.some((old) => candidates.some((point) => key(point) === key(old) && point.epoch === old.epoch)) ? "first-seen" : advanced ? "advanced" : "matches", issues, state };
9831
+ }
9832
+ function stationVersionObservation(proof, epoch = "") {
9833
+ return { kind: "station", chain: "network", epoch, position: proof.revision, digest: proof.commitId, original: prettyJSON(proof.json) };
9834
+ }
9835
+ function checkpointVersionObservation(checkpoint) {
9836
+ return {
9837
+ kind: "checkpoint",
9838
+ chain: "network",
9839
+ epoch: String(checkpoint.epoch),
9840
+ position: checkpoint.sequence,
9841
+ revision: checkpoint.revision,
9842
+ digest: checkpoint.id,
9843
+ final: checkpoint.isFinal,
9844
+ rootDigest: checkpoint.commitId,
9845
+ ...checkpoint.previousId === void 0 ? {} : { previous: checkpoint.previousId },
9846
+ original: prettyJSON(checkpoint.json)
9847
+ };
9848
+ }
9849
+ function priceVersionObservation(statement) {
9850
+ return {
9851
+ kind: "price",
9852
+ chain: statement.chain,
9853
+ epoch: "",
9854
+ position: statement.sequence,
9855
+ digest: statement.digestHex(),
9856
+ ...statement.previous === void 0 ? {} : { previous: statement.previous },
9857
+ original: prettyJSON(statement.json)
9858
+ };
9859
+ }
9860
+ function checkpointRootVersionObservation(checkpoint) {
9861
+ return { kind: "station", chain: "network", epoch: checkpoint.epoch === 1 ? "" : String(checkpoint.epoch), position: checkpoint.revision, digest: checkpoint.commitId, original: prettyJSON(checkpoint.json) };
9862
+ }
9863
+
9864
+ // src/verifier/documentKeys.ts
9865
+ var CHARGING_DOCUMENT_USAGE = "chargeTransparencyRecords";
9866
+ var ALGORITHMS = {
9867
+ 1: "ECDSA-secp256r1",
9868
+ 2: "ECDSA-secp384r1",
9869
+ 3: "ECDSA-secp521r1",
9870
+ 6: "EdDSA-Ed25519",
9871
+ 7: "EdDSA-Ed448",
9872
+ 8: "ECDSA-secp256k1"
9873
+ };
9874
+ function verifyChargingDocumentKeys(context, proof, request) {
9875
+ if (!Number.isFinite(request.at) || request.at > context.evaluatedAt)
9876
+ throw new POIError("Invalid charging-document evaluation time");
9877
+ const parsed = ChargingStationProof.parse(proof);
9878
+ const historical = historicalOperatorContext(context, parsed.roamingNetwork["operator"] ?? null, request.at);
9879
+ const station = verifyChargingStation(historical, parsed, { evseId: request.evseId });
9880
+ if (station.operator["@id"] !== request.operatorId)
9881
+ throw new POIError("The live document CPO is not the operator of the proven EVSE");
9882
+ const keys = station.operatorSigningKeys.filter((key2) => key2.isValidAt(request.at) && key2.maySign(CHARGING_DOCUMENT_USAGE)).map((key2) => {
9883
+ const algorithm = key2.key.curve === null ? key2.key.algorithm?.name : ALGORITHMS[key2.key.curve.id];
9884
+ if (algorithm === void 0)
9885
+ throw new POIError("Unsupported CPO charging-document key algorithm");
9886
+ return { id: key2.id, algorithm, encodings: ["COSE_Key", "CBOR", "base64"], value: requireString(key2.json, "key"), keyUsage: ["signCTRs"] };
9887
+ });
9888
+ if (keys.length === 0)
9889
+ throw new POIError("The proven CPO supplies no valid keys authorized for charging transparency records");
9890
+ return {
9891
+ evseId: request.evseId,
9892
+ chargingStationId: requireString(station.chargingStation, "@id"),
9893
+ operatorId: request.operatorId,
9894
+ at: request.at,
9895
+ commitId: station.commitId,
9896
+ revision: station.revision,
9897
+ keys
9898
+ };
9899
+ }
9900
+
9901
+ // src/verifier/trustTransitions.ts
9902
+ var TRUST_TRANSITION_CONTEXT = "https://open.charging.cloud/contexts/wwcp/trustTransition/v1";
9903
+ var trustStatementDigest = (statement) => toHex(hash("sha256", canonicalJSONBytes(statement.content())));
9904
+ function trustPolicyId(anchor) {
9905
+ return toHex(hash("sha256", canonicalJSONBytes({
9906
+ network: anchor.id,
9907
+ holders: anchor.holders.map((holder) => [...holder]),
9908
+ minConsensus: anchor.minConsensus,
9909
+ urls: anchor.urls.map((url) => new URL(url).href.replace(/\/+$/u, "")).sort()
9910
+ })));
9911
+ }
9912
+ var TrustTransition = class _TrustTransition {
9913
+ constructor(json, sequence, previous, signedAt, validUntil, fromPolicyId, fromStatementDigest, anchor, operator) {
9914
+ this.json = json;
9915
+ this.sequence = sequence;
9916
+ this.previous = previous;
9917
+ this.signedAt = signedAt;
9918
+ this.validUntil = validUntil;
9919
+ this.fromPolicyId = fromPolicyId;
9920
+ this.fromStatementDigest = fromStatementDigest;
9921
+ this.anchor = anchor;
9922
+ this.operator = operator;
9923
+ }
9924
+ json;
9925
+ sequence;
9926
+ previous;
9927
+ signedAt;
9928
+ validUntil;
9929
+ fromPolicyId;
9930
+ fromStatementDigest;
9931
+ anchor;
9932
+ operator;
9933
+ static parse(value) {
9934
+ const json = requireObject(value, "A trust transition");
9935
+ onlyProperties(json, [
9936
+ "@context",
9937
+ "sequence",
9938
+ "previous",
9939
+ "fromPolicyId",
9940
+ "fromStatementDigest",
9941
+ "toAnchor",
9942
+ "toOperator",
9943
+ "signedAt",
9944
+ "validUntil",
9945
+ "checkpoints",
9946
+ "epochChange",
9947
+ "signatures"
9948
+ ], "A trust transition");
9949
+ if (json["@context"] !== TRUST_TRANSITION_CONTEXT || new TextEncoder().encode(prettyJSON(json)).length > 2 * 1024 * 1024)
9950
+ throw new POIError("Unsupported or excessive trust transition");
9951
+ const previous = get(json, "previous");
9952
+ const sequence = requireInteger(json, "sequence", 0, 31);
9953
+ if (previous !== void 0 !== sequence > 0 || previous !== void 0 && (typeof previous !== "string" || !/^[0-9a-f]{64}$/u.test(previous)))
9954
+ throw new POIError("Invalid predecessor of a trust transition");
9955
+ const signedAt = requireTimestamp(json, "signedAt");
9956
+ const validUntil = requireTimestamp(json, "validUntil");
9957
+ if (validUntil < signedAt)
9958
+ throw new POIError("Invalid transition validity");
9959
+ return new _TrustTransition(
9960
+ json,
9961
+ sequence,
9962
+ previous,
9963
+ signedAt,
9964
+ validUntil,
9965
+ requireString(json, "fromPolicyId"),
9966
+ requireString(json, "fromStatementDigest"),
9967
+ TrustAnchor.parse(requireString(json, "toAnchor")),
9968
+ requireObject(json["toOperator"], "The target operator")
9969
+ );
9970
+ }
9971
+ /** What it names, but its signatures. */
9972
+ content() {
9973
+ const { signatures: _, ...content } = this.json;
9974
+ return content;
9975
+ }
9976
+ /** The SHA-256 of the canonical JSON of its content: the `previous` of the next transition. */
9977
+ digest() {
9978
+ return toHex(hash("sha256", canonicalJSONBytes(this.content())));
9979
+ }
9980
+ /** The signature input of a signature: its content with the side, the algorithm and the key of the signature. */
9981
+ signingBytes(side, algorithm, keyId) {
9982
+ return canonicalJSONBytes({ ...this.content(), side, algorithm, keyId });
9983
+ }
9984
+ };
9985
+ var VerifiedTrustTransition = class _VerifiedTrustTransition {
9986
+ constructor(source, context, epoch, checkpoints, first, oldEpoch, signedBy) {
9987
+ this.source = source;
9988
+ this.context = context;
9989
+ this.epoch = epoch;
9990
+ this.checkpoints = checkpoints;
9991
+ this.first = first;
9992
+ this.oldEpoch = oldEpoch;
9993
+ this.signedBy = signedBy;
9994
+ }
9995
+ source;
9996
+ context;
9997
+ epoch;
9998
+ checkpoints;
9999
+ first;
10000
+ oldEpoch;
10001
+ signedBy;
10002
+ static verify(basis, fromOperator, transition, options) {
10003
+ if (!Number.isFinite(options.acceptedAt) || options.acceptedAt < transition.signedAt || options.acceptedAt > transition.validUntil || transition.sequence !== options.sequence || transition.previous !== options.previous)
10004
+ throw new POIError("Transition is expired, out of order or not accepted within its signed interval");
10005
+ if (transition.fromPolicyId !== trustPolicyId(basis.anchor) || transition.fromStatementDigest !== basis.keyStatementDigest || transition.anchor.id !== basis.roamingNetworkId || transition.operator["@id"] !== basis.roamingNetworkOperatorId)
10006
+ throw new POIError("Transition does not continue the exact authorized policy/network/operator");
10007
+ const oldOperator = requireObject(fromOperator, "Previous operator");
10008
+ const oldStatement = KeyStatement.parse(oldOperator["keyStatement"]);
10009
+ OperatorTrust.fromOperatorJSON(basis, oldOperator);
10010
+ const nextStatement = KeyStatement.parse(transition.operator["keyStatement"]);
10011
+ const identities = POIPublicKey.parseAll(transition.operator["identityKeys"], "Target identity keys");
10012
+ const next = TrustContext.create(transition.anchor, nextStatement, { trustedIdentityKeyIds: transition.anchor.keys, otherIdentityKeys: identities, at: transition.signedAt });
10013
+ OperatorTrust.fromOperatorJSON(next, transition.operator);
10014
+ if (next.roamingNetworkOperatorId !== basis.roamingNetworkOperatorId || next.requiredConsensus < basis.requiredConsensus)
10015
+ throw new POIError("A trust transition must not silently reduce the required holder quorum");
10016
+ for (const holder of basis.authorizedAnchor.holders)
10017
+ if (transition.anchor.holders.filter((group) => group.some((id) => holder.includes(id))).length > 1)
10018
+ throw new POIError("A transition splits a previously known holder");
10019
+ const oldPool = [...oldStatement.identityKeys, ...POIPublicKey.parseAll(oldOperator["identityKeys"], "Previous identity keys")];
10020
+ const nextPool = [...nextStatement.identityKeys, ...identities];
10021
+ const old = /* @__PURE__ */ new Set();
10022
+ const target = /* @__PURE__ */ new Set();
10023
+ for (const raw of requireArray(transition.json["signatures"], "Transition signatures")) {
10024
+ const signature = requireObject(raw, "A transition signature");
10025
+ onlyProperties(signature, ["side", "algorithm", "keyId", "value"], "A transition signature");
10026
+ const side = requireString(signature, "side");
10027
+ const algorithm = requireString(signature, "algorithm");
10028
+ const id = requireString(signature, "keyId");
10029
+ if (side !== "old" && side !== "new")
10030
+ throw new POIError("Invalid transition signature side");
10031
+ const groups = side === "old" ? basis.trustedHolders : next.trustedHolders;
10032
+ const pool = side === "old" ? oldPool : nextPool;
10033
+ const holder = groups.findIndex((group) => group.includes(id));
10034
+ const key2 = pool.find((key3) => key3.id === id);
10035
+ if (key2?.isValidAt(transition.signedAt) !== true)
10036
+ throw new POIError("Transition signer is not an authorized valid identity key");
10037
+ verifySignature(algorithm, key2.key, transition.signingBytes(side, algorithm, id), requireString(signature, "value"));
10038
+ if (holder >= 0)
10039
+ (side === "old" ? old : target).add(holder);
10040
+ }
10041
+ if (old.size < basis.requiredConsensus || target.size < next.requiredConsensus)
10042
+ throw new POIError("Both old and new holder quorums must sign the trust transition");
10043
+ const checkpoints = (transition.json["checkpoints"] === void 0 ? [] : requireArray(transition.json["checkpoints"], "Transition checkpoints")).map((raw) => {
10044
+ const checkpoint = Checkpoint.parse(raw);
10045
+ verifyAuthorizedCheckpoint(historicalOperatorContext(basis, oldOperator, checkpoint.createdAt), checkpoint, oldOperator);
10046
+ if (checkpoint.epoch !== options.epoch.number)
10047
+ throw new POIError("The transition checkpoint line belongs to another epoch");
10048
+ return checkpoint;
10049
+ });
10050
+ let epoch = options.epoch;
10051
+ let first;
10052
+ if (transition.json["epochChange"] !== void 0) {
10053
+ const change = requireObject(transition.json["epochChange"], "An epoch change");
10054
+ onlyProperties(change, ["genesis", "firstCheckpoint"], "An epoch change");
10055
+ const final = checkpoints.at(-1);
10056
+ if (final?.next === void 0)
10057
+ throw new POIError("An epoch transition needs the signed final checkpoint");
10058
+ if (final.createdAt > transition.signedAt)
10059
+ throw new POIError("The final checkpoint postdates the transition");
10060
+ const genesis = Commit.parse(change["genesis"]);
10061
+ const expected = Commit.genesisOf(basis.roamingNetworkId, final.json["eTags"] ?? null, final.next);
10062
+ first = Checkpoint.parse(change["firstCheckpoint"]);
10063
+ if (genesis.id !== expected.id || !first.isOf(expected) || first.sequence !== 0 || first.epoch !== final.next.number || first.previous?.checkpointId !== final.id || first.previous.epoch !== final.epoch || !sameETags(first.previous.eTags, final.eTags))
10064
+ throw new POIError("The next epoch must start from the exact deterministic genesis and final checkpoint");
10065
+ if ((trustPolicyId(next.anchor) !== trustPolicyId(basis.anchor) || next.keyStatementDigest !== basis.keyStatementDigest) && first.createdAt !== transition.signedAt)
10066
+ throw new POIError("A combined policy/epoch handoff must activate at its first checkpoint time");
10067
+ if (first.createdAt < final.createdAt || first.createdAt > transition.signedAt)
10068
+ throw new POIError("Invalid epoch-transition checkpoint times");
10069
+ const trust = OperatorTrust.fromOperatorJSON(historicalOperatorContext(next, transition.operator, first.createdAt), transition.operator);
10070
+ for (const profile of [options.epoch.profiles, final.next.profiles]) {
10071
+ const signatures = first.signatures.filter((signature) => signature.role === "roamingNetworkOperator" && profile.allowsSignatureAlgorithm(signature.algorithm));
10072
+ trust.verifyOperatorSignatures(signatures, (signature, key2) => first?.verifySignature(signature, key2), "epoch-transition checkpoint");
10073
+ }
10074
+ for (const signature of first.signatures)
10075
+ if (!options.epoch.profiles.allowsSignatureAlgorithm(signature.algorithm) && !final.next.profiles.allowsSignatureAlgorithm(signature.algorithm))
10076
+ throw new POIError("An epoch-transition signature uses neither accepted profile");
10077
+ epoch = { ...final.next, startedAt: first.createdAt };
10078
+ }
10079
+ return new _VerifiedTrustTransition(
10080
+ transition,
10081
+ TrustContext.withEpoch(next, epoch, [...basis.epochs.filter((item) => item.number !== epoch.number), epoch]),
10082
+ epoch,
10083
+ checkpoints,
10084
+ first,
10085
+ options.epoch,
10086
+ { old: [...old], next: [...target] }
10087
+ );
10088
+ }
10089
+ };
10090
+ function applyVerifiedTrustTransition(before, verified, at) {
10091
+ const id = verified.source.digest();
10092
+ const known = before.transitions?.find((entry) => entry.id === id);
10093
+ const policy = compareObservedVersions(
10094
+ before,
10095
+ [{
10096
+ kind: "policy",
10097
+ chain: "trust",
10098
+ epoch: "",
10099
+ position: verified.source.sequence,
10100
+ digest: id,
10101
+ original: prettyJSON(verified.source.json),
10102
+ ...verified.source.previous === void 0 ? {} : { previous: verified.source.previous }
10103
+ }],
10104
+ "historical",
10105
+ at
10106
+ );
10107
+ if (!policy.accepted)
10108
+ return policy;
10109
+ before = policy.state;
10110
+ if (known !== void 0)
10111
+ return { accepted: true, status: "matches", issues: [], state: before };
10112
+ if ((before.transitions?.length ?? 0) !== verified.source.sequence)
10113
+ throw new POIError("The observed journal has another transition predecessor");
10114
+ const oldPoints = verified.checkpoints.flatMap((checkpoint) => [checkpointVersionObservation(checkpoint), checkpointRootVersionObservation(checkpoint)]);
10115
+ const prior = compareObservedVersions(before, oldPoints, "current", at);
10116
+ if (!prior.accepted)
10117
+ return prior;
10118
+ let state = prior.state;
10119
+ if (verified.first !== void 0) {
10120
+ const points = [checkpointVersionObservation(verified.first), checkpointRootVersionObservation(verified.first)];
10121
+ const checked = compareObservedVersions(state, points, "historical", at);
10122
+ if (!checked.accepted)
10123
+ return checked;
10124
+ const heads = { ...checked.state.heads };
10125
+ for (const point of points)
10126
+ heads[JSON.stringify([point.kind, point.chain])] = { epoch: point.epoch, position: point.position, digest: point.digest };
10127
+ state = { ...checked.state, heads };
10128
+ }
10129
+ return {
10130
+ accepted: true,
10131
+ status: "advanced",
10132
+ issues: [],
10133
+ state: {
10134
+ ...state,
10135
+ transitions: [
10136
+ ...state.transitions ?? [],
10137
+ {
10138
+ id,
10139
+ sequence: verified.source.sequence,
10140
+ fromPolicyId: verified.source.fromPolicyId,
10141
+ toPolicyId: trustPolicyId(verified.source.anchor),
10142
+ fromEpoch: verified.oldEpoch.number,
10143
+ toEpoch: verified.epoch.number,
10144
+ original: prettyJSON(verified.source.json)
10145
+ }
10146
+ ]
10147
+ }
10148
+ };
10149
+ }
10150
+ var initialTrustEpoch = () => ({ number: 1, profiles: EpochProfiles.FIRST });
9025
10151
 
9026
- export { ATTESTATION_CONTEXT, ArchiveSegment, Attestation, CHANGE_SET_CONTEXT, CHECKPOINT_CONTEXT2 as CHECKPOINT_CONTEXT, CHILD_ARRAYS, COMMIT_CONTEXT2 as COMMIT_CONTEXT, CONTEXT_PREFIX, ChangeSet, ChargingStationProof, Checkpoint, CheckpointProof, Commit, DCCMeasurementResult, DCCResult, DCCStatement, DCC_PERFORMANCE_LOCATIONS, DCOC_MODULES, DDoCNotifiedBody, Decimal, DigitalCalibrationCertificate, DigitalCertificateOfConformity, DigitalDeclarationOfConformity, EPOCH_CONTEXT, EPOCH_LINK_CONTEXT, EPOCH_PROFILES_CONTEXT, EV_TALER_EXCHANGE_CONTEXT, EV_TALER_MERCHANT_CONTEXT, EV_TALER_PAYMENT_MEANS, EV_TALER_PAYMENT_OPTION, EpochProfiles, FIRST_EPOCH, History, KEY_STATEMENT_CONTEXT, KeyStatement, KeyUsages, LIVE_TOPICS, MAX_DECIMAL_SCALE, MAX_PRICE_STATEMENT_SEQUENCE, MERGE_CHANGE_SET_ID_PREFIX, OperatorTrust, POIClient, POIError, POIPublicKey, PROOF_CONTEXT, PriceStatement, PriceStatementProof, QIDigitalDocument, QIDigitalItem, QIDigitalLegislation, QIDigitalManufacturer, QIDigitalModel, QIDigitalPerson, QIDigitalQuantity, QIDigitalSignature, QIDigitalStandard, QI_DIGITAL_CONFORMITIES, ROAMING_NETWORK_CONTEXT, RUNTIME_STATUS_KINDS, RoamingNetwork, RoamingNetworkOperator, RuntimeStatus, SIGNATURE_ALGORITHMS, SIGNING_KEY_CERTIFICATE_CONTEXT, STATUS_UPDATE_CONTEXT, STYX_CANONICAL_JSON, SignatureRoles, SigningKeyCertificate, TRUST_ANCHOR_CONTEXT, TRUST_ANCHOR_QR_PREFIX, TrustAnchor, TrustContext, applyChangeSet, approvesQualitySystem, auditPath, canonicalJSON, canonicalJSONBytes, canonicalString, cborETag, cborToJson, changeSetIdentification, check, checkOwnership, clone, commitsRoot, decodeDeterministicCbor, describeVersion, deterministicCbor, effectiveChargingStationOperator, effectiveContent, effectiveTreeETag, entityDigest, etagToJSON, etagToText, evTalerExchangesAt, evTalerMerchantOf, evTalerTariffOf, fromBase45, fromBase64, fromHex, fromJavaScript, get, hash, isArray, isObject, isTalerPublicKey, jsonETag, jsonToCbor, leafHash, liveRequest, merkleRoot, merkleRootOfLeafHashes, nodeHash, parseArchiveSegmentInfo, parseETag, parseETags, parseEpoch, parseEpochLink, parseEvidence, parseFrame, parseJSON, parseJSONBytes, parseJSONObject, parsePriceStatements, parseStatusUpdate, parseTimestamp, parseTrustedWitnesses, paymentOptionName, prettyJSON, priceStatementChainOf, priceStatementsRoot, prunedTree, qiDigitalContext, rootFromAuditPath, sameETag, sameETags, sameEpoch, sameValue, skeleton, subscribeRequest, toBase45, toBase64, toHex, toTimestamp, verifyChargingStation, verifyCheckpointLine, verifyCheckpointPriceStatements, verifyCheckpointSignatures, verifyEvidence, verifyPriceStatementChain, verifyPriceStatements, verifySegmentLine, verifySignature, verifyVersion };
10152
+ export { ATTESTATION_CONTEXT, ArchiveSegment, Attestation, CHANGE_SET_CONTEXT, CHARGING_DOCUMENT_USAGE, CHECKPOINT_CONTEXT2 as CHECKPOINT_CONTEXT, CHILD_ARRAYS, COMMIT_CONTEXT2 as COMMIT_CONTEXT, CONTEXT_PREFIX, ChangeSet, ChargingStationProof, Checkpoint, CheckpointProof, Commit, DCCMeasurementResult, DCCResult, DCCStatement, DCC_PERFORMANCE_LOCATIONS, DCOC_MODULES, DDoCNotifiedBody, Decimal, DigitalCalibrationCertificate, DigitalCertificateOfConformity, DigitalDeclarationOfConformity, EPOCH_CONTEXT, EPOCH_LINK_CONTEXT, EPOCH_PROFILES_CONTEXT, EV_TALER_EXCHANGE_CONTEXT, EV_TALER_MERCHANT_CONTEXT, EV_TALER_PAYMENT_MEANS, EV_TALER_PAYMENT_OPTION, EpochProfiles, FIRST_EPOCH, HISTORICAL_METER_EVIDENCE_CONTEXT, HistoricalTrustContext, History, KEY_STATEMENT_CONTEXT, KeyStatement, KeyUsages, LIVE_TOPICS, MAX_DECIMAL_SCALE, MAX_PRICE_STATEMENT_SEQUENCE, MERGE_CHANGE_SET_ID_PREFIX, MeasurementKeyError, OperatorTrust, POIClient, POIError, POIPublicKey, PRICE_PUBLICATION_RECEIPT_CONTEXT, PROOF_CONTEXT, PricePublicationReceipt, PriceStatement, PriceStatementProof, QIDigitalDocument, QIDigitalItem, QIDigitalLegislation, QIDigitalManufacturer, QIDigitalModel, QIDigitalPerson, QIDigitalQuantity, QIDigitalSignature, QIDigitalStandard, QI_DIGITAL_CONFORMITIES, ROAMING_NETWORK_CONTEXT, RUNTIME_STATUS_KINDS, RoamingNetwork, RoamingNetworkOperator, RuntimeStatus, SESSION_PRICE_AGREEMENT_CONTEXT, SESSION_PRICE_EVIDENCE_CONTEXT, SIGNATURE_ALGORITHMS, SIGNING_KEY_CERTIFICATE_CONTEXT, SIMPLE_GROSS_PRICE_PROFILE, STATUS_UPDATE_CONTEXT, STYX_CANONICAL_JSON, SessionPriceAgreement, SignatureRoles, SigningKeyCertificate, TRUST_ANCHOR_CONTEXT, TRUST_ANCHOR_QR_PREFIX, TRUST_TRANSITION_CONTEXT, TrustAnchor, TrustContext, TrustTransition, VerifiedTrustTransition, WITNESS_PUBLICATION_POLICY_CONTEXT, applyChangeSet, applyVerifiedTrustTransition, approvesQualitySystem, auditPath, canonicalJSON, canonicalJSONBytes, canonicalString, cborETag, cborToJson, changeSetIdentification, check, checkOwnership, checkpointRootVersionObservation, checkpointVersionObservation, clone, commitsRoot, compareObservedVersions, decodeDeterministicCbor, describeVersion, deterministicCbor, effectiveChargingStationOperator, effectiveContent, effectiveTreeETag, emptyObservedVersions, entityDigest, etagToJSON, etagToText, evTalerExchangesAt, evTalerMerchantOf, evTalerTariffOf, fromBase45, fromBase64, fromHex, fromJavaScript, get, hash, historicalOperatorContext, initialTrustEpoch, isArray, isObject, isTalerPublicKey, jsonETag, jsonToCbor, leafHash, liveRequest, merkleRoot, merkleRootOfLeafHashes, nodeHash, observedVersionsChecksum, parseArchiveSegmentInfo, parseETag, parseETags, parseEpoch, parseEpochLink, parseEvidence, parseFrame, parseHistoricalMeterEvidence, parseJSON, parseJSONBytes, parseJSONObject, parseMeasurementPublicKey, parsePriceStatements, parseSessionPriceEvidence, parseStatusUpdate, parseTimestamp, parseTrustedWitnesses, parseWitnessPublicationPolicy, paymentOptionName, prettyJSON, pricePublicationOfferHashes, priceStatementChainOf, priceStatementsRoot, priceVersionObservation, prunedTree, publicPublicationURL, qiDigitalContext, rootFromAuditPath, sameETag, sameETags, sameEpoch, sameValue, skeleton, stationVersionObservation, subscribeRequest, toBase45, toBase64, toHex, toTimestamp, trustPolicyId, trustStatementDigest, verifyAuthorizedCheckpoint, verifyChargingDocumentKeys, verifyChargingStation, verifyCheckpointLine, verifyCheckpointPriceStatements, verifyCheckpointSignatures, verifyEvidence, verifyHistoricalMeterEvidence, verifyMeterKey, verifyPricePublication, verifyPriceStatementChain, verifyPriceStatements, verifySegmentLine, verifySessionPriceEvidence, verifySignature, verifyVersion };
9027
10153
  //# sourceMappingURL=index.js.map
9028
10154
  //# sourceMappingURL=index.js.map