@red-isbe/isbe-service-did-registry 1.0.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.
Files changed (51) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +129 -0
  3. package/dist/index.d.ts +26 -0
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/index.js +26 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/src/ServiceDidRegistryContext.d.ts +40 -0
  8. package/dist/src/ServiceDidRegistryContext.d.ts.map +1 -0
  9. package/dist/src/ServiceDidRegistryContext.js +42 -0
  10. package/dist/src/ServiceDidRegistryContext.js.map +1 -0
  11. package/dist/src/ServiceDidRegistryQueries.d.ts +127 -0
  12. package/dist/src/ServiceDidRegistryQueries.d.ts.map +1 -0
  13. package/dist/src/ServiceDidRegistryQueries.js +226 -0
  14. package/dist/src/ServiceDidRegistryQueries.js.map +1 -0
  15. package/dist/src/ServiceDidRegistryTransactions.d.ts +90 -0
  16. package/dist/src/ServiceDidRegistryTransactions.d.ts.map +1 -0
  17. package/dist/src/ServiceDidRegistryTransactions.js +134 -0
  18. package/dist/src/ServiceDidRegistryTransactions.js.map +1 -0
  19. package/dist/src/ServiceDidTransactionExecutor.d.ts +31 -0
  20. package/dist/src/ServiceDidTransactionExecutor.d.ts.map +1 -0
  21. package/dist/src/ServiceDidTransactionExecutor.js +37 -0
  22. package/dist/src/ServiceDidTransactionExecutor.js.map +1 -0
  23. package/dist/src/abi/IServiceDidRegistry.abi.d.ts +578 -0
  24. package/dist/src/abi/IServiceDidRegistry.abi.d.ts.map +1 -0
  25. package/dist/src/abi/IServiceDidRegistry.abi.js +741 -0
  26. package/dist/src/abi/IServiceDidRegistry.abi.js.map +1 -0
  27. package/dist/src/serviceDid.types.d.ts +78 -0
  28. package/dist/src/serviceDid.types.d.ts.map +1 -0
  29. package/dist/src/serviceDid.types.js +16 -0
  30. package/dist/src/serviceDid.types.js.map +1 -0
  31. package/dist/src/types.d.ts +35 -0
  32. package/dist/src/types.d.ts.map +1 -0
  33. package/dist/src/types.js +16 -0
  34. package/dist/src/types.js.map +1 -0
  35. package/dist/src/utils/blockchain.utils.d.ts +49 -0
  36. package/dist/src/utils/blockchain.utils.d.ts.map +1 -0
  37. package/dist/src/utils/blockchain.utils.js +102 -0
  38. package/dist/src/utils/blockchain.utils.js.map +1 -0
  39. package/dist/src/utils/did.utils.d.ts +20 -0
  40. package/dist/src/utils/did.utils.d.ts.map +1 -0
  41. package/dist/src/utils/did.utils.js +57 -0
  42. package/dist/src/utils/did.utils.js.map +1 -0
  43. package/dist/src/utils/serviceDid.utils.d.ts +71 -0
  44. package/dist/src/utils/serviceDid.utils.d.ts.map +1 -0
  45. package/dist/src/utils/serviceDid.utils.js +142 -0
  46. package/dist/src/utils/serviceDid.utils.js.map +1 -0
  47. package/dist/src/utils/serviceDocument.utils.d.ts +61 -0
  48. package/dist/src/utils/serviceDocument.utils.d.ts.map +1 -0
  49. package/dist/src/utils/serviceDocument.utils.js +101 -0
  50. package/dist/src/utils/serviceDocument.utils.js.map +1 -0
  51. package/package.json +45 -0
@@ -0,0 +1,142 @@
1
+ /**
2
+ * Copyright (c) 2025 Comunidad de Madrid & Alastria
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ *
7
+ * You may obtain a copy of the License at
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ import { AbiCoder, getAddress, keccak256 } from "ethers";
17
+ import { didToHex } from "./did.utils.js";
18
+ const SERVICE_PREFIX = "did:isbe:svc:";
19
+ const ORGANISATIONAL_PREFIX = "did:isbe:";
20
+ /**
21
+ * Derives the on-chain identifier of a service identity.
22
+ *
23
+ * Mirrors `ServiceDidRegistryInternal._computeServiceDid` exactly. The encoding is
24
+ * `abi.encode`, NOT `solidityPacked`: `abi.encode` pads the nonce to 32 bytes, and a
25
+ * packed encoding produces a different digest. Getting this wrong does not break any
26
+ * transaction — it breaks resolution, silently, which is far worse.
27
+ *
28
+ * @param controllerDidHex Parent DID as bytes32 hex
29
+ * @param nonce Monotonic counter of the controller, starting at 1
30
+ */
31
+ export function computeServiceDidHex(controllerDidHex, nonce) {
32
+ if (!/^0x[0-9a-fA-F]{64}$/.test(controllerDidHex)) {
33
+ throw new Error("The 'controllerDidHex' parameter must be a 32-byte hexadecimal string");
34
+ }
35
+ const nonceValue = BigInt(nonce);
36
+ if (nonceValue <= 0n) {
37
+ throw new Error("The 'nonce' parameter must be greater than zero");
38
+ }
39
+ return keccak256(AbiCoder.defaultAbiCoder().encode(["bytes32", "uint64"], [controllerDidHex, nonceValue]));
40
+ }
41
+ /**
42
+ * Builds the readable identifier of a service identity.
43
+ *
44
+ * The parent's own segment is carried verbatim, which is what lets a reader recover
45
+ * the controlling DID from the service DID alone, with no lookup. A digest of the
46
+ * parent would not be reversible.
47
+ *
48
+ * @param controllerDid Parent DID, as did:isbe:<network>:<segment>
49
+ * @param nonce Monotonic counter of the controller
50
+ */
51
+ export function buildServiceDid(controllerDid, nonce) {
52
+ const { network, segment } = splitOrganisationalDid(controllerDid);
53
+ return `${SERVICE_PREFIX}${network}:${segment}:${BigInt(nonce).toString()}`;
54
+ }
55
+ /**
56
+ * Recovers the parent DID and the nonce from a service DID string.
57
+ *
58
+ * Entirely local: no network call. This is the direction that matters for resolution,
59
+ * and the reason the identifier carries the parent's segment rather than a digest.
60
+ *
61
+ * Note the asymmetry — the reverse is not possible. A service identifier is a full
62
+ * keccak digest, so the DID string cannot be recovered from it without reading the
63
+ * record from the chain to learn its controller and nonce.
64
+ */
65
+ export function parseServiceDid(serviceDid) {
66
+ if (!serviceDid || !serviceDid.startsWith(SERVICE_PREFIX)) {
67
+ throw new Error(`The 'serviceDid' parameter must start with '${SERVICE_PREFIX}'`);
68
+ }
69
+ const rest = serviceDid.slice(SERVICE_PREFIX.length).split("#")[0].split("?")[0];
70
+ const parts = rest.split(":");
71
+ if (parts.length !== 3) {
72
+ throw new Error("The 'serviceDid' parameter must have the form did:isbe:svc:<network>:<segment>:<nonce>");
73
+ }
74
+ const [network, segment, nonceText] = parts;
75
+ if (!network || !segment) {
76
+ throw new Error("The service DID carries an empty network or segment");
77
+ }
78
+ if (!/^[0-9]+$/.test(nonceText)) {
79
+ throw new Error("The nonce of a service DID must be a decimal integer");
80
+ }
81
+ const nonce = BigInt(nonceText);
82
+ if (nonce <= 0n) {
83
+ throw new Error("The nonce of a service DID starts at one");
84
+ }
85
+ return {
86
+ network,
87
+ controllerDid: `${ORGANISATIONAL_PREFIX}${network}:${segment}`,
88
+ nonce,
89
+ };
90
+ }
91
+ /**
92
+ * Resolves a service DID string to the identifier the contract stores.
93
+ *
94
+ * Local: parses the string, converts the parent DID to bytes32 and derives the digest.
95
+ */
96
+ export function serviceDidToHex(serviceDid) {
97
+ const { controllerDid, nonce } = parseServiceDid(serviceDid);
98
+ return computeServiceDidHex(didToHex(controllerDid), nonce);
99
+ }
100
+ /**
101
+ * Derives the address of a signing key from its coordinates.
102
+ *
103
+ * Reproduces the derivation the organisational registry applies to its own keys —
104
+ * keccak256 of the 64 coordinate bytes, truncated to 20 — so both registries agree on
105
+ * the address of a given key.
106
+ *
107
+ * Returned checksummed, so it compares equal to what ethers hands back from a contract
108
+ * call or from a wallet. A lowercase form would be the same address and still fail a
109
+ * naive equality check.
110
+ */
111
+ export function signingKeyAddress(pubKeyX, pubKeyY) {
112
+ const x = stripHex(pubKeyX, "pubKeyX");
113
+ const y = stripHex(pubKeyY, "pubKeyY");
114
+ const digest = keccak256(`0x${x}${y}`);
115
+ return getAddress(`0x${digest.slice(-40)}`);
116
+ }
117
+ /** Reassembles the two coordinates into the 65-byte uncompressed encoding. */
118
+ export function toUncompressedPublicKey(pubKeyX, pubKeyY) {
119
+ return `0x04${stripHex(pubKeyX, "pubKeyX")}${stripHex(pubKeyY, "pubKeyY")}`;
120
+ }
121
+ /** Splits an organisational DID into its network discriminator and its segment. */
122
+ function splitOrganisationalDid(controllerDid) {
123
+ if (!controllerDid || !controllerDid.startsWith(ORGANISATIONAL_PREFIX)) {
124
+ throw new Error(`The 'controllerDid' parameter must start with '${ORGANISATIONAL_PREFIX}'`);
125
+ }
126
+ const parts = controllerDid
127
+ .slice(ORGANISATIONAL_PREFIX.length)
128
+ .split("#")[0]
129
+ .split("?")[0]
130
+ .split(":");
131
+ if (parts.length !== 2 || !parts[0] || !parts[1]) {
132
+ throw new Error("The 'controllerDid' parameter must have the form did:isbe:<network>:<segment>");
133
+ }
134
+ return { network: parts[0], segment: parts[1] };
135
+ }
136
+ function stripHex(value, name) {
137
+ if (!/^0x[0-9a-fA-F]{64}$/.test(value)) {
138
+ throw new Error(`The '${name}' parameter must be a 32-byte hexadecimal string`);
139
+ }
140
+ return value.slice(2);
141
+ }
142
+ //# sourceMappingURL=serviceDid.utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serviceDid.utils.js","sourceRoot":"","sources":["../../../src/utils/serviceDid.utils.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Copyright (c) 2025 Comunidad de Madrid & Alastria
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ *
7
+ * You may obtain a copy of the License at
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ /**
17
+ * The free-form part of a service identity.
18
+ *
19
+ * A Service DID has a fixed minimum — controller, key, curve, label, expiry — and on
20
+ * top of it each organisation may add whatever fields it needs: a service endpoint, a
21
+ * company name, a description. Those extra fields travel as one JSON object, stored
22
+ * on-chain next to the record, and a resolver adds them to the DID Document.
23
+ *
24
+ * The registry treats the document as an opaque string. Everything here is the
25
+ * contract this library puts around it: it must be a JSON object, it cannot redefine
26
+ * what the registry itself asserts, and it has to fit the on-chain size limit.
27
+ */
28
+ /** Members of a service document. Values are any JSON. */
29
+ export type ServiceDocument = Record<string, unknown>;
30
+ /** Largest document the registry accepts, in UTF-8 bytes. Mirrors the contract. */
31
+ export declare const MAX_SERVICE_DOCUMENT_BYTES = 4096;
32
+ /**
33
+ * Members a service document may not set, because the resolver derives them from the
34
+ * registry. Letting a document define its own `id`, `controller` or keys would let it
35
+ * claim an identity, or a key, the registry never bound to it.
36
+ */
37
+ export declare const RESERVED_DOCUMENT_KEYS: readonly string[];
38
+ /** Raised when a service document cannot be stored as given. */
39
+ export declare class ServiceDocumentError extends Error {
40
+ constructor(message: string);
41
+ }
42
+ /**
43
+ * Turns a service document into the string the registry stores.
44
+ *
45
+ * `null`, `undefined` and `{}` all become the empty string, which is what clears a
46
+ * document on-chain.
47
+ *
48
+ * @throws ServiceDocumentError when the document is not a JSON object, sets a reserved
49
+ * member, or exceeds `MAX_SERVICE_DOCUMENT_BYTES` once serialised
50
+ */
51
+ export declare function serializeServiceDocument(document: ServiceDocument | null | undefined): string;
52
+ /**
53
+ * Reads a stored service document back.
54
+ *
55
+ * Tolerant by design: the contract accepts any string, so a document written without
56
+ * this library may not be JSON, or may set reserved members. Resolution must not
57
+ * break because of it, so anything unreadable yields `null` and reserved members are
58
+ * dropped.
59
+ */
60
+ export declare function parseServiceDocument(raw: string): ServiceDocument | null;
61
+ //# sourceMappingURL=serviceDocument.utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serviceDocument.utils.d.ts","sourceRoot":"","sources":["../../../src/utils/serviceDocument.utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;EAcE;AAEF;;;;;;;;;;;GAWG;AAEH,0DAA0D;AAC1D,MAAM,MAAM,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEtD,mFAAmF;AACnF,eAAO,MAAM,0BAA0B,OAAO,CAAC;AAE/C;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,EAAE,SAAS,MAAM,EAYnD,CAAC;AAEF,gEAAgE;AAChE,qBAAa,oBAAqB,SAAQ,KAAK;gBACjC,OAAO,EAAE,MAAM;CAI5B;AAMD;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,eAAe,GAAG,IAAI,GAAG,SAAS,GAC3C,MAAM,CA2BR;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI,CAgBxE"}
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Copyright (c) 2025 Comunidad de Madrid & Alastria
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ *
7
+ * You may obtain a copy of the License at
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ /** Largest document the registry accepts, in UTF-8 bytes. Mirrors the contract. */
17
+ export const MAX_SERVICE_DOCUMENT_BYTES = 4096;
18
+ /**
19
+ * Members a service document may not set, because the resolver derives them from the
20
+ * registry. Letting a document define its own `id`, `controller` or keys would let it
21
+ * claim an identity, or a key, the registry never bound to it.
22
+ */
23
+ export const RESERVED_DOCUMENT_KEYS = [
24
+ "@context",
25
+ "id",
26
+ "controller",
27
+ "verificationMethod",
28
+ "authentication",
29
+ "assertionMethod",
30
+ "capabilityInvocation",
31
+ "capabilityDelegation",
32
+ "keyAgreement",
33
+ "deactivated",
34
+ "reason",
35
+ ];
36
+ /** Raised when a service document cannot be stored as given. */
37
+ export class ServiceDocumentError extends Error {
38
+ constructor(message) {
39
+ super(message);
40
+ this.name = "ServiceDocumentError";
41
+ }
42
+ }
43
+ function isPlainObject(value) {
44
+ return typeof value === "object" && value !== null && !Array.isArray(value);
45
+ }
46
+ /**
47
+ * Turns a service document into the string the registry stores.
48
+ *
49
+ * `null`, `undefined` and `{}` all become the empty string, which is what clears a
50
+ * document on-chain.
51
+ *
52
+ * @throws ServiceDocumentError when the document is not a JSON object, sets a reserved
53
+ * member, or exceeds `MAX_SERVICE_DOCUMENT_BYTES` once serialised
54
+ */
55
+ export function serializeServiceDocument(document) {
56
+ if (document === null || document === undefined)
57
+ return "";
58
+ if (!isPlainObject(document)) {
59
+ throw new ServiceDocumentError("The service document must be a JSON object.");
60
+ }
61
+ const reserved = Object.keys(document).filter((key) => RESERVED_DOCUMENT_KEYS.includes(key));
62
+ if (reserved.length > 0) {
63
+ throw new ServiceDocumentError(`The service document cannot set ${reserved.join(", ")}: the registry derives ${reserved.length === 1 ? "it" : "them"}.`);
64
+ }
65
+ if (Object.keys(document).length === 0)
66
+ return "";
67
+ const serialized = JSON.stringify(document);
68
+ const bytes = new TextEncoder().encode(serialized).length;
69
+ if (bytes > MAX_SERVICE_DOCUMENT_BYTES) {
70
+ throw new ServiceDocumentError(`The service document is ${bytes} bytes; the registry accepts at most ${MAX_SERVICE_DOCUMENT_BYTES}.`);
71
+ }
72
+ return serialized;
73
+ }
74
+ /**
75
+ * Reads a stored service document back.
76
+ *
77
+ * Tolerant by design: the contract accepts any string, so a document written without
78
+ * this library may not be JSON, or may set reserved members. Resolution must not
79
+ * break because of it, so anything unreadable yields `null` and reserved members are
80
+ * dropped.
81
+ */
82
+ export function parseServiceDocument(raw) {
83
+ if (!raw)
84
+ return null;
85
+ let parsed;
86
+ try {
87
+ parsed = JSON.parse(raw);
88
+ }
89
+ catch {
90
+ return null;
91
+ }
92
+ if (!isPlainObject(parsed))
93
+ return null;
94
+ const clean = {};
95
+ for (const [key, value] of Object.entries(parsed)) {
96
+ if (!RESERVED_DOCUMENT_KEYS.includes(key))
97
+ clean[key] = value;
98
+ }
99
+ return Object.keys(clean).length > 0 ? clean : null;
100
+ }
101
+ //# sourceMappingURL=serviceDocument.utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serviceDocument.utils.js","sourceRoot":"","sources":["../../../src/utils/serviceDocument.utils.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "@red-isbe/isbe-service-did-registry",
3
+ "version": "1.0.0",
4
+ "description": "A library for interacting with the ISBE Service DID Registry: operational identities under did:isbe:svc:",
5
+ "main": "dist/index.js",
6
+ "type": "module",
7
+ "types": "dist/index.d.ts",
8
+ "files": [
9
+ "dist"
10
+ ],
11
+ "publishConfig": {
12
+ "access": "public"
13
+ },
14
+ "scripts": {
15
+ "test": "npm run test:unit && npm run test:esm",
16
+ "test:unit": "jest --config jest.config.ts",
17
+ "test:esm": "NODE_OPTIONS=--experimental-vm-modules jest -c jest.esm.config.ts",
18
+ "build": "tsc",
19
+ "prepublishOnly": "npm run build && npm test"
20
+ },
21
+ "keywords": [
22
+ "did",
23
+ "did:isbe",
24
+ "did:isbe:svc",
25
+ "service-did"
26
+ ],
27
+ "author": "ISBE",
28
+ "license": "Apache-2.0",
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "git+https://github.com/red-isbe/isbe-service-did-lib.git"
32
+ },
33
+ "devDependencies": {
34
+ "@red-isbe/did-isbe-registry": "^2.0.1",
35
+ "@types/jest": "^30.0.0",
36
+ "@types/node": "^24.9.1",
37
+ "jest": "^30.1.3",
38
+ "ts-jest": "^29.4.4",
39
+ "typescript": "^5.9.3"
40
+ },
41
+ "dependencies": {
42
+ "ethers": "^6.15.0",
43
+ "multiformats": "^13.4.2"
44
+ }
45
+ }