@parity/product-sdk 0.22.0 → 0.23.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/chunk-BRZNHTLQ.js +89 -0
- package/dist/chunk-BRZNHTLQ.js.map +1 -0
- package/dist/{chunk-6BI66XUJ.js → chunk-DHRCWMY5.js} +9 -10
- package/dist/chunk-DHRCWMY5.js.map +1 -0
- package/dist/core/index.d.ts +2 -2
- package/dist/core/index.js +2 -2
- package/dist/dotns-bswWAmR2.d.ts +136 -0
- package/dist/identity/index.d.ts +502 -45
- package/dist/identity/index.js +800 -32
- package/dist/identity/index.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +2 -2
- package/dist/react/index.d.ts +2 -2
- package/dist/react/index.js +2 -2
- package/dist/testing/index.d.ts +2 -2
- package/dist/{types-DTgyMIi0.d.ts → types-B5Zx4RNq.d.ts} +1 -1
- package/package.json +10 -10
- package/src/core/createApp.ts +10 -8
- package/src/identity/dotns-abis.test.ts +43 -0
- package/src/identity/dotns-abis.ts +309 -0
- package/src/identity/dotns-addresses.test.ts +536 -0
- package/src/identity/dotns-addresses.ts +320 -0
- package/src/identity/dotns-errors.ts +36 -0
- package/src/identity/dotns-namehash.test.ts +155 -0
- package/src/identity/dotns-namehash.ts +149 -0
- package/src/identity/dotns-owner.test.ts +92 -0
- package/src/identity/dotns-registry.test.ts +1309 -0
- package/src/identity/dotns-registry.ts +868 -0
- package/src/identity/dotns.test.ts +128 -0
- package/src/identity/dotns.ts +91 -78
- package/src/identity/index.test.ts +43 -0
- package/src/identity/index.ts +40 -10
- package/src/identity/types.ts +29 -25
- package/src/individuality/contract.test.ts +272 -2
- package/dist/chunk-6BI66XUJ.js.map +0 -1
- package/dist/chunk-BIKT6X54.js +0 -70
- package/dist/chunk-BIKT6X54.js.map +0 -1
- package/dist/dotns-DIaptENj.d.ts +0 -167
- package/src/identity/product-account.test.ts +0 -129
- package/src/identity/product-account.ts +0 -115
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
// Copyright 2026 Parity Technologies (UK) Ltd.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
import { describe, expect, test } from "vitest";
|
|
4
|
+
import { stripSuffix } from "./dotns-namehash.js";
|
|
5
|
+
import { isResolvableDotNsName, isValidDotNsName, normalizeDotNsName } from "./dotns.js";
|
|
6
|
+
|
|
7
|
+
// The two deployments these helpers actually have to serve. Every case below
|
|
8
|
+
// runs against `.paseo` as well as `.dot`, because a helper that only behaves
|
|
9
|
+
// on a four-character suffix is the defect being fixed.
|
|
10
|
+
const DOT = ".dot";
|
|
11
|
+
const PASEO = ".paseo";
|
|
12
|
+
|
|
13
|
+
describe("stripSuffix", () => {
|
|
14
|
+
test("removes the suffix by its own length", () => {
|
|
15
|
+
expect(stripSuffix("alice.paseo", PASEO)).toBe("alice");
|
|
16
|
+
expect(stripSuffix("alice.dot", DOT)).toBe("alice");
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test("a six-character suffix does not lose two characters to a hardcoded 4", () => {
|
|
20
|
+
// `slice(0, -4)` returns "alice.pa" here, which then hashes and queries
|
|
21
|
+
// as the subname `pa` under `alice`.
|
|
22
|
+
expect(stripSuffix("alice.paseo", PASEO)).not.toBe("alice.pa");
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test("leaves a name that does not carry the suffix untouched", () => {
|
|
26
|
+
expect(stripSuffix("alice", PASEO)).toBe("alice");
|
|
27
|
+
expect(stripSuffix("alice.dot", PASEO)).toBe("alice.dot");
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test("keeps subname structure", () => {
|
|
31
|
+
expect(stripSuffix("bob.alice.paseo", PASEO)).toBe("bob.alice");
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test("a bare suffix strips to the empty label", () => {
|
|
35
|
+
expect(stripSuffix(".paseo", PASEO)).toBe("");
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
describe("normalizeDotNsName", () => {
|
|
40
|
+
test("appends the deployment's suffix to a bare label", () => {
|
|
41
|
+
expect(normalizeDotNsName("alice", PASEO)).toBe("alice.paseo");
|
|
42
|
+
expect(normalizeDotNsName("alice", DOT)).toBe("alice.dot");
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test("is idempotent for a name that already carries the suffix", () => {
|
|
46
|
+
expect(normalizeDotNsName("alice.paseo", PASEO)).toBe("alice.paseo");
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test("leaves a foreign suffix alone instead of burying it", () => {
|
|
50
|
+
// The rule this pins: appending only when there is no dot at all means
|
|
51
|
+
// `alice.dot` survives to be refused by validation. The `endsWith`
|
|
52
|
+
// rule would produce `alice.dot.paseo`, which is a well-formed subname
|
|
53
|
+
// and would resolve silently to a node nobody owns.
|
|
54
|
+
expect(normalizeDotNsName("alice.dot", PASEO)).toBe("alice.dot");
|
|
55
|
+
expect(normalizeDotNsName("alice.dot", PASEO)).not.toBe("alice.dot.paseo");
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test("leaves a bare multi-label name alone", () => {
|
|
59
|
+
// Recorded behaviour change: this used to become `bob.alice.dot` and
|
|
60
|
+
// resolve. It is now left as-is and refused by validation, because
|
|
61
|
+
// telling `bob.alice` from `alice.dot` needs a list of known TLDs.
|
|
62
|
+
expect(normalizeDotNsName("bob.alice", PASEO)).toBe("bob.alice");
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test("lowercases, trims, and drops a single trailing root dot", () => {
|
|
66
|
+
// Matches `normalize_host` in truapi-server, so a name arriving from a
|
|
67
|
+
// URL keys the same way here as it does in the host.
|
|
68
|
+
expect(normalizeDotNsName(" Alice.PASEO. ", PASEO)).toBe("alice.paseo");
|
|
69
|
+
expect(normalizeDotNsName("ALICE", PASEO)).toBe("alice.paseo");
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test("drops only one trailing dot, so a doubled dot stays visible", () => {
|
|
73
|
+
// `alice.paseo..` is malformed and must reach validation as such
|
|
74
|
+
// rather than being quietly repaired into a valid name.
|
|
75
|
+
expect(normalizeDotNsName("alice.paseo..", PASEO)).toBe("alice.paseo.");
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
describe("isValidDotNsName", () => {
|
|
80
|
+
test("accepts one label under the deployment's suffix", () => {
|
|
81
|
+
expect(isValidDotNsName("alice.paseo", PASEO)).toBe(true);
|
|
82
|
+
expect(isValidDotNsName("alice.dot", DOT)).toBe(true);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test("refuses another deployment's suffix", () => {
|
|
86
|
+
expect(isValidDotNsName("alice.dot", PASEO)).toBe(false);
|
|
87
|
+
expect(isValidDotNsName("alice.paseo", DOT)).toBe(false);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test("refuses a subname, which the registrar cannot mint", () => {
|
|
91
|
+
expect(isValidDotNsName("bob.alice.paseo", PASEO)).toBe(false);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test("applies the label rule to the label, not to the suffix's length", () => {
|
|
95
|
+
// With a hardcoded `slice(0, -4)` the label here would be `dim2.pa`,
|
|
96
|
+
// which fails the label regex and would reject a registrable name.
|
|
97
|
+
expect(isValidDotNsName("dim2.paseo", PASEO)).toBe(true);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
test("keeps the on-chain label rules", () => {
|
|
101
|
+
expect(isValidDotNsName("ab.paseo", PASEO)).toBe(false); // under 3 chars
|
|
102
|
+
expect(isValidDotNsName("-alice.paseo", PASEO)).toBe(false); // edge hyphen
|
|
103
|
+
expect(isValidDotNsName("al ice.paseo", PASEO)).toBe(false); // space
|
|
104
|
+
expect(isValidDotNsName(`${"a".repeat(64)}.paseo`, PASEO)).toBe(false); // over 63
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
describe("isResolvableDotNsName", () => {
|
|
109
|
+
test("accepts a subname, which the registry supports even though the registrar does not", () => {
|
|
110
|
+
expect(isResolvableDotNsName("bob.alice.paseo", PASEO)).toBe(true);
|
|
111
|
+
expect(isResolvableDotNsName("alice.paseo", PASEO)).toBe(true);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
test("refuses another deployment's suffix", () => {
|
|
115
|
+
expect(isResolvableDotNsName("bob.alice.dot", PASEO)).toBe(false);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
test("extracts the labels by suffix length, not by a constant 4", () => {
|
|
119
|
+
// `slice(0, -4)` would split `dim2.pa` into ["dim2", "pa"] and reject
|
|
120
|
+
// it on the 3-character minimum.
|
|
121
|
+
expect(isResolvableDotNsName("dim2.paseo", PASEO)).toBe(true);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
test("refuses an empty label anywhere in the chain", () => {
|
|
125
|
+
expect(isResolvableDotNsName("bob..alice.paseo", PASEO)).toBe(false);
|
|
126
|
+
expect(isResolvableDotNsName(".paseo", PASEO)).toBe(false);
|
|
127
|
+
});
|
|
128
|
+
});
|
package/src/identity/dotns.ts
CHANGED
|
@@ -3,12 +3,16 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* DotNS (Polkadot Name Service) utilities
|
|
5
5
|
*
|
|
6
|
-
*
|
|
6
|
+
* Name validation and normalization. Every helper here takes the deployment's
|
|
7
|
+
* TLD suffix rather than assuming `.dot`: the TLD is fixed per network when the
|
|
8
|
+
* contracts initialise, so the same string is a valid name on one deployment
|
|
9
|
+
* and meaningless on another. `resolveTld` in `./dotns-registry.js` is what
|
|
10
|
+
* asks the chain which suffix is in play.
|
|
7
11
|
*/
|
|
8
12
|
|
|
9
13
|
import { accountIdBytes } from "@parity/product-sdk-address";
|
|
10
14
|
import { bytesToHex, hexToBytes } from "@parity/product-sdk-crypto";
|
|
11
|
-
import {
|
|
15
|
+
import { stripSuffix } from "./dotns-namehash.js";
|
|
12
16
|
import type {
|
|
13
17
|
ChainDefinition,
|
|
14
18
|
PalletsTypedef,
|
|
@@ -18,9 +22,6 @@ import type {
|
|
|
18
22
|
StorageDescriptor,
|
|
19
23
|
TxDescriptor,
|
|
20
24
|
} from "polkadot-api";
|
|
21
|
-
import type { DotNsRecord } from "./types.js";
|
|
22
|
-
|
|
23
|
-
const log = createLogger("identity");
|
|
24
25
|
|
|
25
26
|
type AnyDescriptorEntry<T> = Record<string, Record<string, T>>;
|
|
26
27
|
|
|
@@ -71,100 +72,103 @@ export type PeopleUsernameQueryApi = {
|
|
|
71
72
|
};
|
|
72
73
|
|
|
73
74
|
/**
|
|
74
|
-
*
|
|
75
|
+
* Whether `name` is a single registrable label under `suffix`.
|
|
76
|
+
*
|
|
77
|
+
* `suffix` is the deployment's own TLD, including its leading dot, as
|
|
78
|
+
* `resolveTld` reports it — `".dot"` on Paseo Asset Hub Previewnet, `".paseo"`
|
|
79
|
+
* on Next V2. It is required rather than defaulted, because a name is only
|
|
80
|
+
* valid *relative to a deployment*: `alice.dot` is registrable on one network
|
|
81
|
+
* and meaningless on the other, and a default would silently pick one.
|
|
75
82
|
*
|
|
76
|
-
* @param name - Name to validate
|
|
77
|
-
* @
|
|
83
|
+
* @param name - Name to validate, already normalized by {@link normalizeDotNsName}
|
|
84
|
+
* @param suffix - The deployment's TLD suffix, e.g. `".paseo"`
|
|
78
85
|
*/
|
|
79
|
-
export function isValidDotNsName(name: string): boolean {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
const label = name.slice(0, -4);
|
|
83
|
-
if (label.length < 3 || label.length > 63) return false;
|
|
84
|
-
return /^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/.test(label);
|
|
86
|
+
export function isValidDotNsName(name: string, suffix: string): boolean {
|
|
87
|
+
if (!name.endsWith(suffix)) return false;
|
|
88
|
+
return isValidLabel(stripSuffix(name, suffix));
|
|
85
89
|
}
|
|
86
90
|
|
|
87
91
|
/**
|
|
88
|
-
*
|
|
92
|
+
* One canonical DNS label, matching the on-chain rule.
|
|
89
93
|
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
94
|
+
* `StringUtils.isSingleLabel` allows lowercase ASCII, digits and non-edge
|
|
95
|
+
* hyphens up to 63 characters, and the registrar additionally requires 3.
|
|
92
96
|
*/
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
normalized += ".dot";
|
|
97
|
-
}
|
|
98
|
-
return normalized;
|
|
97
|
+
function isValidLabel(label: string): boolean {
|
|
98
|
+
if (label.length < 3 || label.length > 63) return false;
|
|
99
|
+
return /^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/.test(label);
|
|
99
100
|
}
|
|
100
101
|
|
|
101
102
|
/**
|
|
102
|
-
*
|
|
103
|
+
* Whether a name can be resolved: one or more valid labels under `suffix`.
|
|
103
104
|
*
|
|
104
|
-
* @
|
|
105
|
-
*
|
|
106
|
-
*
|
|
107
|
-
*
|
|
105
|
+
* Broader than {@link isValidDotNsName} on purpose. The registrar only mints
|
|
106
|
+
* single labels, so registration keeps the stricter rule, but the registry
|
|
107
|
+
* supports subnodes and `namehash` hashes them, so `bob.alice.paseo` is
|
|
108
|
+
* resolvable even though it is not registrable.
|
|
108
109
|
*
|
|
109
|
-
* @param name -
|
|
110
|
-
* @
|
|
110
|
+
* @param name - Name to check, already normalized by {@link normalizeDotNsName}
|
|
111
|
+
* @param suffix - The deployment's TLD suffix, e.g. `".paseo"`
|
|
111
112
|
*/
|
|
112
|
-
export
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
log.warn("Invalid DotNS name", { name });
|
|
117
|
-
return null;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
log.debug("Resolving DotNS name", { name: normalized });
|
|
121
|
-
|
|
122
|
-
// TODO: Implement via PAPI query to DotNS pallet
|
|
123
|
-
throw new Error(
|
|
124
|
-
"resolveDotNs() is not yet implemented. " +
|
|
125
|
-
"This is a skeleton for the Product SDK structure.",
|
|
126
|
-
);
|
|
113
|
+
export function isResolvableDotNsName(name: string, suffix: string): boolean {
|
|
114
|
+
if (!name.endsWith(suffix)) return false;
|
|
115
|
+
const labels = stripSuffix(name, suffix).split(".");
|
|
116
|
+
return labels.length > 0 && labels.every(isValidLabel);
|
|
127
117
|
}
|
|
128
118
|
|
|
129
119
|
/**
|
|
130
|
-
*
|
|
120
|
+
* Normalize a DotNS name for the deployment identified by `suffix`.
|
|
131
121
|
*
|
|
132
|
-
*
|
|
133
|
-
*
|
|
134
|
-
*
|
|
122
|
+
* Case-folds, applies NFC, trims surrounding whitespace, and drops a single
|
|
123
|
+
* trailing root dot, so `Alice.PASEO.` keys the same as `alice.paseo`. This
|
|
124
|
+
* mirrors `normalize_host` in truapi-server, which is what turns a URL into a
|
|
125
|
+
* dotNS identifier — a name arriving from navigation has to key identically
|
|
126
|
+
* here or the two layers disagree about the same input.
|
|
135
127
|
*
|
|
136
|
-
* @
|
|
137
|
-
*
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
// TODO: Implement via PAPI query to DotNS pallet
|
|
143
|
-
throw new Error(
|
|
144
|
-
"reverseDotNs() is not yet implemented. " +
|
|
145
|
-
"This is a skeleton for the Product SDK structure.",
|
|
146
|
-
);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* Check if a DotNS name is available for registration.
|
|
128
|
+
* The NFC step is defensive only: {@link isValidLabel} and the on-chain
|
|
129
|
+
* `StringUtils.isSingleLabel` both allow lowercase ASCII alone, so no name that
|
|
130
|
+
* survives validation can differ between its composed and decomposed spellings.
|
|
131
|
+
* It costs nothing and it keeps this function honest if the label rule ever
|
|
132
|
+
* widens.
|
|
151
133
|
*
|
|
152
|
-
*
|
|
134
|
+
* **The suffix is appended only when `name` contains no dot at all.** A bare
|
|
135
|
+
* label is the friendly path and gets the deployment's suffix; anything already
|
|
136
|
+
* carrying a dot is left exactly as written, so a name from another deployment
|
|
137
|
+
* survives to be refused by validation rather than being buried under our own
|
|
138
|
+
* suffix. Appending on `!endsWith(suffix)` instead would turn `alice.dot` into
|
|
139
|
+
* `alice.dot.paseo`, which is a well-formed subname and would resolve silently
|
|
140
|
+
* to a node nobody owns.
|
|
153
141
|
*
|
|
154
|
-
*
|
|
155
|
-
*
|
|
142
|
+
* The cost of that rule: a bare multi-label name such as `bob.alice` is no
|
|
143
|
+
* longer completed to `bob.alice.paseo`. Distinguishing it from `alice.dot`
|
|
144
|
+
* would require a hardcoded list of every network's TLD, and there are already
|
|
145
|
+
* more TLDs deployed than any such list contains.
|
|
146
|
+
*
|
|
147
|
+
* @param name - Name to normalize
|
|
148
|
+
* @param suffix - The deployment's TLD suffix, e.g. `".paseo"`
|
|
156
149
|
*/
|
|
157
|
-
export
|
|
158
|
-
const
|
|
159
|
-
|
|
150
|
+
export function normalizeDotNsName(name: string, suffix: string): string {
|
|
151
|
+
const folded = name.trim().normalize("NFC").toLowerCase();
|
|
152
|
+
const normalized = folded.endsWith(".") ? folded.slice(0, -1) : folded;
|
|
153
|
+
return normalized.includes(".") ? normalized : normalized + suffix;
|
|
160
154
|
}
|
|
161
155
|
|
|
156
|
+
// `resolveDotNs` / `reverseDotNs` / `isDotNsAvailable` now live in
|
|
157
|
+
// `./dotns-registry.ts` (real `Result`-returning registry calls), replacing the
|
|
158
|
+
// throwing skeletons that used to sit here. See that module + the design doc.
|
|
159
|
+
|
|
162
160
|
/**
|
|
163
|
-
* Resolve a People / People Lite username to its owning
|
|
161
|
+
* Resolve a People / People Lite username to its owning account.
|
|
164
162
|
*
|
|
165
163
|
* Queries `Resources.UsernameOwnerOf` on the caller-supplied typed-api fragment.
|
|
166
|
-
* The returned value is the
|
|
167
|
-
*
|
|
164
|
+
* The returned value is the SS58 address the storage yields, or `null` when no
|
|
165
|
+
* owner is registered for that username.
|
|
166
|
+
*
|
|
167
|
+
* Returns SS58 rather than hex because that is both the form the storage yields
|
|
168
|
+
* and the form `Resources.Consumers` is keyed by, so reading an account's
|
|
169
|
+
* usernames from this result needs no conversion. Callers that want the raw
|
|
170
|
+
* 32-byte key use `accountIdBytes` from `@parity/product-sdk-address`, and
|
|
171
|
+
* {@link accountIdToHex} for the hex form.
|
|
168
172
|
*
|
|
169
173
|
* The `username` is UTF-8 encoded as-is — no normalization is applied. Pass
|
|
170
174
|
* the exact byte string the chain stores (typically with the `.dot` suffix).
|
|
@@ -176,13 +180,11 @@ export async function isDotNsAvailable(name: string): Promise<boolean> {
|
|
|
176
180
|
export async function resolvePeopleUsernameOwner(
|
|
177
181
|
username: string,
|
|
178
182
|
peopleApi: PeopleUsernameQueryApi,
|
|
179
|
-
): Promise
|
|
183
|
+
): Promise<SS58String | null> {
|
|
180
184
|
const owner = await peopleApi.query.Resources.UsernameOwnerOf.getValue(
|
|
181
185
|
new TextEncoder().encode(username),
|
|
182
186
|
);
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
return accountIdBytesToHex(accountIdBytes(owner));
|
|
187
|
+
return owner ?? null;
|
|
186
188
|
}
|
|
187
189
|
|
|
188
190
|
function assertHex(value: string): `0x${string}` {
|
|
@@ -200,9 +202,20 @@ export function accountIdHexToBytes(accountId: `0x${string}`): Uint8Array {
|
|
|
200
202
|
return bytes;
|
|
201
203
|
}
|
|
202
204
|
|
|
203
|
-
|
|
205
|
+
/** Throws on any input that is not exactly 32 bytes, rather than padding it. */
|
|
206
|
+
export function accountIdBytesToHex(bytes: Uint8Array): `0x${string}` {
|
|
204
207
|
if (bytes.length !== 32) {
|
|
205
208
|
throw new Error(`Expected 32-byte AccountId, got ${bytes.length} bytes`);
|
|
206
209
|
}
|
|
207
210
|
return `0x${bytesToHex(bytes)}`;
|
|
208
211
|
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* The inverse of {@link accountIdHexToBytes}.
|
|
215
|
+
*
|
|
216
|
+
* No in-repo caller: this is the migration path for consumers, after
|
|
217
|
+
* `resolvePeopleUsernameOwner` changed from hex to SS58. Not dead code.
|
|
218
|
+
*/
|
|
219
|
+
export function accountIdToHex(address: string): `0x${string}` {
|
|
220
|
+
return accountIdBytesToHex(accountIdBytes(address));
|
|
221
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// Copyright 2026 Parity Technologies (UK) Ltd.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
/**
|
|
4
|
+
* Guards the identity export surface: what must be there, and what must stay gone.
|
|
5
|
+
*/
|
|
6
|
+
import { describe, expect, it } from "vitest";
|
|
7
|
+
import * as identity from "./index.js";
|
|
8
|
+
|
|
9
|
+
// The check below is a runtime one, so it cannot see a removed type. If this
|
|
10
|
+
// type comes back, the suppression goes unused and typecheck fails.
|
|
11
|
+
// @ts-expect-error must stay removed
|
|
12
|
+
import type { ContextAliasInfo } from "./types.js";
|
|
13
|
+
|
|
14
|
+
describe("the identity export surface", () => {
|
|
15
|
+
// Both sets threw or misled on every call, so no working consumer could
|
|
16
|
+
// exist. Deleting turns a runtime problem into a compile error.
|
|
17
|
+
it("no longer exports the ring stubs or the context-alias helpers", () => {
|
|
18
|
+
for (const name of [
|
|
19
|
+
"deriveAnonymousAlias",
|
|
20
|
+
"createRingProof",
|
|
21
|
+
"verifyRingProof",
|
|
22
|
+
"deriveContextAlias",
|
|
23
|
+
"verifyContextAlias",
|
|
24
|
+
]) {
|
|
25
|
+
expect(name in identity).toBe(false);
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("still exports the DotNS helpers", () => {
|
|
30
|
+
for (const name of [
|
|
31
|
+
"resolveDotNs",
|
|
32
|
+
"reverseDotNs",
|
|
33
|
+
"isDotNsAvailable",
|
|
34
|
+
"resolvePeopleUsernameOwner",
|
|
35
|
+
"isValidDotNsName",
|
|
36
|
+
"normalizeDotNsName",
|
|
37
|
+
"accountIdHexToBytes",
|
|
38
|
+
"accountIdToHex",
|
|
39
|
+
]) {
|
|
40
|
+
expect(name in identity).toBe(true);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
});
|
package/src/identity/index.ts
CHANGED
|
@@ -3,33 +3,63 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* @parity/product-sdk/identity
|
|
5
5
|
*
|
|
6
|
-
* Identity utilities: DotNS name resolution
|
|
7
|
-
* helpers.
|
|
6
|
+
* Identity utilities: DotNS name resolution and registry access.
|
|
8
7
|
*
|
|
9
|
-
* For
|
|
8
|
+
* For Ring VRF aliases and proofs, use `SignerManager`'s
|
|
10
9
|
* `getProductAccountAlias` and `createRingVRFProof` from
|
|
11
10
|
* `@parity/product-sdk-signer`.
|
|
12
11
|
*/
|
|
13
12
|
|
|
14
|
-
// DotNS utilities
|
|
13
|
+
// DotNS utilities (pure helpers + People-chain username path)
|
|
15
14
|
export {
|
|
16
15
|
isValidDotNsName,
|
|
16
|
+
isResolvableDotNsName,
|
|
17
17
|
normalizeDotNsName,
|
|
18
|
-
resolveDotNs,
|
|
19
|
-
reverseDotNs,
|
|
20
|
-
isDotNsAvailable,
|
|
21
18
|
accountIdHexToBytes,
|
|
19
|
+
accountIdToHex,
|
|
22
20
|
resolvePeopleUsernameOwner,
|
|
23
21
|
} from "./dotns.js";
|
|
24
22
|
export type { PeopleUsernameChain, PeopleUsernameQueryApi } from "./dotns.js";
|
|
25
23
|
|
|
26
|
-
//
|
|
27
|
-
export {
|
|
24
|
+
// DotNS registry reads + writes (Revive contract)
|
|
25
|
+
export {
|
|
26
|
+
resolveDotNs,
|
|
27
|
+
reverseDotNs,
|
|
28
|
+
isDotNsAvailable,
|
|
29
|
+
setDotNsRecord,
|
|
30
|
+
prepareDotNsRegistration,
|
|
31
|
+
} from "./dotns-registry.js";
|
|
32
|
+
export type {
|
|
33
|
+
DotNsClientOptions,
|
|
34
|
+
RegisterDotNsArgs,
|
|
35
|
+
SetRecordArgs,
|
|
36
|
+
DotNsRegistration,
|
|
37
|
+
} from "./dotns-registry.js";
|
|
38
|
+
export { DotNsError } from "./dotns-errors.js";
|
|
39
|
+
export type { DotNsErrorReason } from "./dotns-errors.js";
|
|
40
|
+
export {
|
|
41
|
+
namehash,
|
|
42
|
+
dotNsTld,
|
|
43
|
+
isConsistentDotNsTld,
|
|
44
|
+
stripSuffix,
|
|
45
|
+
DOT_NODE,
|
|
46
|
+
DOT_TLD,
|
|
47
|
+
} from "./dotns-namehash.js";
|
|
48
|
+
export type { DotNsTld } from "./dotns-namehash.js";
|
|
49
|
+
export { POP_STATUS, DOTNS_ADDRESSES } from "./dotns-abis.js";
|
|
50
|
+
|
|
51
|
+
// Where those addresses come from: the pinned table, or the gateway walk.
|
|
52
|
+
export {
|
|
53
|
+
DOTNS_REGISTRY_KEYS,
|
|
54
|
+
discoverDotNsAddresses,
|
|
55
|
+
resolveDotNsAddresses,
|
|
56
|
+
verifyDotNsAddresses,
|
|
57
|
+
} from "./dotns-addresses.js";
|
|
58
|
+
export type { DotNsAddresses, DotNsGatewayQueryApi } from "./dotns-addresses.js";
|
|
28
59
|
|
|
29
60
|
// Types
|
|
30
61
|
export type {
|
|
31
62
|
DotNsRecord,
|
|
32
|
-
ContextAliasInfo,
|
|
33
63
|
VerificationResult,
|
|
34
64
|
OnChainIdentity,
|
|
35
65
|
} from "./types.js";
|
package/src/identity/types.ts
CHANGED
|
@@ -2,39 +2,43 @@
|
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
/**
|
|
4
4
|
* Identity module types
|
|
5
|
-
*
|
|
6
|
-
* Types for DotNS name resolution and context-alias derivation
|
|
7
5
|
*/
|
|
8
6
|
|
|
9
7
|
/** DotNS name resolution result */
|
|
10
8
|
export interface DotNsRecord {
|
|
11
|
-
/**
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
/**
|
|
10
|
+
* H160 the name resolves to, `0x` and 20 bytes. Not SS58: DotNS is a set of
|
|
11
|
+
* Revive contracts and both address fields come back as EVM addresses.
|
|
12
|
+
* Convert with `h160ToSs58` from `@parity/product-sdk-address` if a
|
|
13
|
+
* Substrate-shaped address is needed.
|
|
14
|
+
*
|
|
15
|
+
* Absent when the name is registered but has no forward record yet, the
|
|
16
|
+
* state of every name just after registration. An unregistered name is
|
|
17
|
+
* `null` from `resolveDotNs`, not a record.
|
|
18
|
+
*/
|
|
19
|
+
address?: `0x${string}`;
|
|
20
|
+
/**
|
|
21
|
+
* Name that was resolved, normalized: lowercased, and carrying the
|
|
22
|
+
* deployment's own TLD suffix — `.paseo` on Paseo Asset Hub Next V2, `.dot`
|
|
23
|
+
* on Previewnet. A bare label passed in comes back suffixed.
|
|
24
|
+
*
|
|
25
|
+
* Note for a **lite-person** registrant: the registry stores their label
|
|
26
|
+
* flattened, so `alice.42` is registered as `alice42` and this field reads
|
|
27
|
+
* `alice42.paseo`. Pass the flattened spelling when resolving; the dotted
|
|
28
|
+
* one derives a different node and finds nothing.
|
|
29
|
+
*/
|
|
14
30
|
name: string;
|
|
15
|
-
/**
|
|
16
|
-
owner: string
|
|
17
|
-
/**
|
|
31
|
+
/** H160 of the node's owner. Not SS58, same as {@link DotNsRecord.address}. */
|
|
32
|
+
owner: `0x${string}`;
|
|
33
|
+
/**
|
|
34
|
+
* Expiration timestamp, if the deployment has one.
|
|
35
|
+
*
|
|
36
|
+
* Always absent today: the Paseo Asset Hub registrar exposes no expiry
|
|
37
|
+
* getter, so nothing populates this.
|
|
38
|
+
*/
|
|
18
39
|
expiresAt?: number;
|
|
19
40
|
}
|
|
20
41
|
|
|
21
|
-
/**
|
|
22
|
-
* Context alias info: a deterministic, context-bound alias derived from a parent account
|
|
23
|
-
*
|
|
24
|
-
* @deprecated Returned by `deriveContextAlias`, whose addresses no key can spend.
|
|
25
|
-
* Removal: `@parity/product-sdk` 0.23.0.
|
|
26
|
-
*/
|
|
27
|
-
export interface ContextAliasInfo {
|
|
28
|
-
/** Alias SS58 address */
|
|
29
|
-
address: string;
|
|
30
|
-
/** H160 EVM address */
|
|
31
|
-
h160Address: `0x${string}`;
|
|
32
|
-
/** Parent account address */
|
|
33
|
-
parentAddress: string;
|
|
34
|
-
/** Context string used for derivation */
|
|
35
|
-
context: string;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
42
|
/** Identity verification result */
|
|
39
43
|
export interface VerificationResult {
|
|
40
44
|
/** Whether identity is verified */
|