@novasamatech/host-api-wrapper 0.9.2 → 0.9.3
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/README.md +38 -8
- package/dist/accounts.d.ts +198 -9
- package/dist/accounts.js +106 -5
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -262,32 +262,62 @@ if (accountResult.isOk()) {
|
|
|
262
262
|
console.log('Public key:', account.publicKey);
|
|
263
263
|
}
|
|
264
264
|
|
|
265
|
-
// Ring VRF: a contextual alias and a proof are addressed by
|
|
266
|
-
// `context` (`[productId, suffix]`) and a `ring`
|
|
267
|
-
//
|
|
268
|
-
// to the same 32-byte
|
|
265
|
+
// Ring VRF: a contextual alias and a proof are addressed by an explicit member
|
|
266
|
+
// key handle, a product-scoped `context` (`[productId, suffix]`) and a `ring`
|
|
267
|
+
// location on a chain (RFC 0004, amended by RFC 0024). The suffix is the same
|
|
268
|
+
// selector as an account's derivation index and expands to the same 32-byte
|
|
269
|
+
// value (RFC 0022).
|
|
269
270
|
const context: ProofContext = ['product.dot', 0]; // [productId, selector]
|
|
270
271
|
const ring = {
|
|
271
272
|
chainId: '0x…', // 32-byte chain genesis hash
|
|
272
273
|
junctions: [{ tag: 'PalletInstance', value: 42 }],
|
|
273
274
|
};
|
|
274
275
|
|
|
275
|
-
//
|
|
276
|
-
|
|
276
|
+
// Register a key your own product owns for that ring. Permissionless and
|
|
277
|
+
// prompt-free — ownership is the calling product, never a parameter. Returns the
|
|
278
|
+
// member public key. Registering the same index for another ring extends the
|
|
279
|
+
// existing entry rather than adding a second one.
|
|
280
|
+
const registerResult = await accounts.registerRingVrfKey(0, ring);
|
|
281
|
+
|
|
282
|
+
// Discover another product's keys. Handles are opaque: select by the rings an
|
|
283
|
+
// entry declares, NEVER by index — the index is the owner's implementation
|
|
284
|
+
// detail and hardcoding it breaks the moment the owner rotates or adds a key.
|
|
285
|
+
const keysResult = await accounts.listRingVrfKeys('peopl.dot'); // 'Anonymized' by default
|
|
286
|
+
const personKey = keysResult.isOk()
|
|
287
|
+
? keysResult.value.find(entry => entry.rings.some(r => r.chainId === ring.chainId))
|
|
288
|
+
: undefined;
|
|
289
|
+
|
|
290
|
+
// A key handle for one of your own keys — for a foreign key use the handle from
|
|
291
|
+
// `listRingVrfKeys` verbatim instead.
|
|
292
|
+
import { ringVrfKeyHandle } from '@novasamatech/host-api-wrapper';
|
|
293
|
+
|
|
294
|
+
const ownHandle = ringVrfKeyHandle('product.dot', 0);
|
|
295
|
+
|
|
296
|
+
// Get the contextual alias for that (handle, context, ring).
|
|
297
|
+
const aliasResult = await accounts.getContextualAlias(ownHandle, context, ring);
|
|
277
298
|
|
|
278
299
|
if (aliasResult.isOk()) {
|
|
279
300
|
const { context: contextBytes, alias } = aliasResult.value;
|
|
280
301
|
console.log('Alias:', alias);
|
|
281
302
|
}
|
|
282
303
|
|
|
283
|
-
// Create a ring VRF proof binding `message
|
|
284
|
-
|
|
304
|
+
// Create a ring VRF proof binding `message` with an explicit member key.
|
|
305
|
+
// A proof is a bearer token for its context's alias, so a *foreign* handle is
|
|
306
|
+
// admitted only when its owner allowlisted your product in its manifest — there
|
|
307
|
+
// is no user-prompt fallback, and you get `NotAllowlisted` otherwise.
|
|
308
|
+
const proofResult = await accounts.createRingVRFProof(ownHandle, context, ring, new Uint8Array([0x48, 0x69]));
|
|
285
309
|
|
|
286
310
|
if (proofResult.isOk()) {
|
|
287
311
|
const { proof, contextualAlias, ringIndex, ringRevision } = proofResult.value;
|
|
288
312
|
console.log('Proof:', proof, 'at ring index', ringIndex, 'revision', ringRevision);
|
|
289
313
|
}
|
|
290
314
|
|
|
315
|
+
// Sign with the member key itself rather than proving membership anonymously
|
|
316
|
+
// (RFC 0024). No context and no ring: it derives no alias and proves nothing, so
|
|
317
|
+
// there is nothing for either to scope. Verified against the member public key,
|
|
318
|
+
// which makes the signature linkable to every other use of that key.
|
|
319
|
+
const signatureResult = await accounts.ringVrfSign(ownHandle, new Uint8Array([0x48, 0x69]));
|
|
320
|
+
|
|
291
321
|
// sr25519 VRF signature over a product account (RFC-0023). The transcript is a
|
|
292
322
|
// recipe — a root label plus ordered `(label, value)` items — that the host
|
|
293
323
|
// replays verbatim (`Transcript::new(label)` then one `append_message` per item)
|
package/dist/accounts.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AccountConnectionStatus as AccountConnectionStatusCodec, AccountSelector, CodecType, LegacyAccount as LegacyAccountCodec, ProductAccountId as ProductAccountIdCodec, Subscription, Transport, VrfTranscriptItem as VrfTranscriptItemCodec } from '@novasamatech/host-api';
|
|
1
|
+
import type { AccountConnectionStatus as AccountConnectionStatusCodec, AccountSelector, CodecType, LegacyAccount as LegacyAccountCodec, ProductAccountId as ProductAccountIdCodec, RegisteredRingVrfKey as RegisteredRingVrfKeyCodec, RingVrfKeyDisclosure as RingVrfKeyDisclosureCodec, RingVrfKeyHandle as RingVrfKeyHandleCodec, Subscription, Transport, VrfTranscriptItem as VrfTranscriptItemCodec } from '@novasamatech/host-api';
|
|
2
2
|
import { RingLocation } from '@novasamatech/host-api';
|
|
3
3
|
import type { PolkadotSigner } from 'polkadot-api';
|
|
4
4
|
export type { AccountSelector } from '@novasamatech/host-api';
|
|
@@ -18,6 +18,29 @@ export type ProductAccount = {
|
|
|
18
18
|
* product account's.
|
|
19
19
|
*/
|
|
20
20
|
export type ProofContext = [productId: string, suffix: AccountSelector];
|
|
21
|
+
/**
|
|
22
|
+
* Public name of a registered ring VRF key (RFC-0024).
|
|
23
|
+
*
|
|
24
|
+
* Deliberately kept in wire form: the index is the owning product's
|
|
25
|
+
* implementation detail, so a handle obtained from {@link
|
|
26
|
+
* createAccountsProvider}'s `listRingVrfKeys` must be passed through opaquely.
|
|
27
|
+
* **Never hardcode another product's index** — select by declared ring instead;
|
|
28
|
+
* hardcoding breaks the moment the owner rotates or adds a key. Use
|
|
29
|
+
* {@link ringVrfKeyHandle} to name a key your own product owns.
|
|
30
|
+
*/
|
|
31
|
+
export type RingVrfKeyHandle = CodecType<typeof RingVrfKeyHandleCodec>;
|
|
32
|
+
/** A ring VRF key registry entry (RFC-0024). */
|
|
33
|
+
export type RegisteredRingVrfKey = CodecType<typeof RegisteredRingVrfKeyCodec>;
|
|
34
|
+
/** How much of a registry entry to ask for: `'Anonymized'` or `'PublicKey'` (RFC-0024). */
|
|
35
|
+
export type RingVrfKeyDisclosure = CodecType<typeof RingVrfKeyDisclosureCodec>;
|
|
36
|
+
/**
|
|
37
|
+
* Builds a {@link RingVrfKeyHandle} for a key inside `owner`'s ring VRF domain.
|
|
38
|
+
*
|
|
39
|
+
* Intended for naming your *own* keys — the ones you passed to
|
|
40
|
+
* `registerRingVrfKey`. For a foreign key, take the handle from
|
|
41
|
+
* `listRingVrfKeys` instead of constructing one.
|
|
42
|
+
*/
|
|
43
|
+
export declare function ringVrfKeyHandle(owner: string, index: AccountSelector): RingVrfKeyHandle;
|
|
21
44
|
export type LegacyAccount = CodecType<typeof LegacyAccountCodec>;
|
|
22
45
|
export type AccountConnectionStatus = CodecType<typeof AccountConnectionStatusCodec>;
|
|
23
46
|
/** One `transcript.append_message(label, value)` call replayed by the host (RFC-0023). */
|
|
@@ -38,19 +61,87 @@ export declare const createAccountsProvider: (transport?: Transport) => {
|
|
|
38
61
|
}, import("@novasamatech/scale").CodecError<undefined, "RequestCredentialsErr::NotConnected"> | import("@novasamatech/scale").CodecError<undefined, "RequestCredentialsErr::Rejected"> | import("@novasamatech/scale").CodecError<undefined, "RequestCredentialsErr::DomainNotValid"> | import("@novasamatech/scale").CodecError<{
|
|
39
62
|
reason: string;
|
|
40
63
|
}, "RequestCredentialsErr::Unknown">>;
|
|
41
|
-
|
|
64
|
+
/**
|
|
65
|
+
* Registers a ring VRF key this product owns, declaring the ring it is
|
|
66
|
+
* intended for (RFC-0024).
|
|
67
|
+
*
|
|
68
|
+
* Ownership is the calling product, never a parameter, so this is
|
|
69
|
+
* permissionless and prompt-free. Registering the same `index` for another
|
|
70
|
+
* `ring` extends the existing entry rather than creating a second one.
|
|
71
|
+
*
|
|
72
|
+
* Registration declares *intent*, not membership: it says "this is the key
|
|
73
|
+
* I will use for that ring", not "the user is a person". Membership is
|
|
74
|
+
* still discovered only by attempting a proof.
|
|
75
|
+
*/
|
|
76
|
+
registerRingVrfKey(index: AccountSelector, ring: CodecType<typeof RingLocation>): import("neverthrow").ResultAsync<Uint8Array<ArrayBufferLike>, import("@novasamatech/scale").CodecError<undefined, "RegisterRingVrfKeyErr::NotConnected"> | import("@novasamatech/scale").CodecError<undefined, "RegisterRingVrfKeyErr::Rejected"> | import("@novasamatech/scale").CodecError<{
|
|
77
|
+
reason: string;
|
|
78
|
+
}, "RegisterRingVrfKeyErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "RegisterRingVrfKeyErr::RingNotFound">>;
|
|
79
|
+
/**
|
|
80
|
+
* Lists the ring VRF registry entries owned by `owner` — this product or
|
|
81
|
+
* another one (RFC-0024).
|
|
82
|
+
*
|
|
83
|
+
* Listing your own keys is permissionless; a foreign `owner` needs a grant
|
|
84
|
+
* or produces a user prompt. `'PublicKey'` disclosure additionally returns
|
|
85
|
+
* the member public key, which is linkable across every ring it appears in
|
|
86
|
+
* and so is permissioned cross-product.
|
|
87
|
+
*
|
|
88
|
+
* Select the entry you want by the rings it declares, never by index.
|
|
89
|
+
*/
|
|
90
|
+
listRingVrfKeys(owner: string, disclosure?: RingVrfKeyDisclosure): import("neverthrow").ResultAsync<{
|
|
91
|
+
handle: [string, {
|
|
92
|
+
tag: "Index";
|
|
93
|
+
value: number;
|
|
94
|
+
} | {
|
|
95
|
+
tag: "Raw";
|
|
96
|
+
value: Uint8Array<ArrayBufferLike>;
|
|
97
|
+
}];
|
|
98
|
+
rings: {
|
|
99
|
+
chainId: `0x${string}`;
|
|
100
|
+
junctions: ({
|
|
101
|
+
tag: "PalletInstance";
|
|
102
|
+
value: number;
|
|
103
|
+
} | {
|
|
104
|
+
tag: "CollectionId";
|
|
105
|
+
value: Uint8Array<ArrayBufferLike>;
|
|
106
|
+
})[];
|
|
107
|
+
}[];
|
|
108
|
+
publicKey: Uint8Array<ArrayBufferLike> | undefined;
|
|
109
|
+
}[], import("@novasamatech/scale").CodecError<undefined, "ListRingVrfKeysErr::NotConnected"> | import("@novasamatech/scale").CodecError<undefined, "ListRingVrfKeysErr::Rejected"> | import("@novasamatech/scale").CodecError<{
|
|
110
|
+
reason: string;
|
|
111
|
+
}, "ListRingVrfKeysErr::Unknown">>;
|
|
112
|
+
/**
|
|
113
|
+
* Reads the contextual alias `keyHandle` resolves to under `context` in `ring`
|
|
114
|
+
* (RFC-0004, amended by RFC-0024).
|
|
115
|
+
*
|
|
116
|
+
* `ring` stays explicit even though the handle carries its declared rings,
|
|
117
|
+
* because a key may be registered for several and the caller must say which
|
|
118
|
+
* the alias is against; the host fails with `KeyNotInRing` otherwise.
|
|
119
|
+
*
|
|
120
|
+
* Check `ringRevision` on each use of the resulting alias and renew when it
|
|
121
|
+
* has moved — nothing else watches for it.
|
|
122
|
+
*/
|
|
123
|
+
getContextualAlias(keyHandle: RingVrfKeyHandle, context: ProofContext, ring: CodecType<typeof RingLocation>): import("neverthrow").ResultAsync<{
|
|
42
124
|
context: Uint8Array<ArrayBufferLike>;
|
|
43
125
|
alias: Uint8Array<ArrayBufferLike>;
|
|
44
126
|
}, import("@novasamatech/scale").CodecError<undefined, "GetAliasErr::Rejected"> | import("@novasamatech/scale").CodecError<{
|
|
45
127
|
reason: string;
|
|
46
|
-
}, "GetAliasErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "GetAliasErr::RingNotFound"> | import("@novasamatech/scale").CodecError<undefined, "GetAliasErr::NotMember">>;
|
|
128
|
+
}, "GetAliasErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "GetAliasErr::RingNotFound"> | import("@novasamatech/scale").CodecError<undefined, "GetAliasErr::NotMember"> | import("@novasamatech/scale").CodecError<undefined, "GetAliasErr::KeyNotRegistered"> | import("@novasamatech/scale").CodecError<undefined, "GetAliasErr::KeyNotInRing">>;
|
|
47
129
|
getLegacyAccounts(): import("neverthrow").ResultAsync<{
|
|
48
130
|
publicKey: Uint8Array<ArrayBufferLike>;
|
|
49
131
|
name: string | undefined;
|
|
50
132
|
}[], import("@novasamatech/scale").CodecError<undefined, "RequestCredentialsErr::NotConnected"> | import("@novasamatech/scale").CodecError<undefined, "RequestCredentialsErr::Rejected"> | import("@novasamatech/scale").CodecError<undefined, "RequestCredentialsErr::DomainNotValid"> | import("@novasamatech/scale").CodecError<{
|
|
51
133
|
reason: string;
|
|
52
134
|
}, "RequestCredentialsErr::Unknown">>;
|
|
53
|
-
|
|
135
|
+
/**
|
|
136
|
+
* Produces an anonymous ring VRF proof with `keyHandle` under `context` in
|
|
137
|
+
* `ring` (RFC-0004, amended by RFC-0024).
|
|
138
|
+
*
|
|
139
|
+
* A proof is a bearer token for its context's alias, so a *foreign*
|
|
140
|
+
* `keyHandle` is admitted only when the owning product allowlisted this one
|
|
141
|
+
* in its manifest — there is no user-prompt fallback, and the host returns
|
|
142
|
+
* `NotAllowlisted` otherwise.
|
|
143
|
+
*/
|
|
144
|
+
createRingVRFProof(keyHandle: RingVrfKeyHandle, context: ProofContext, ring: CodecType<typeof RingLocation>, message: Uint8Array): import("neverthrow").ResultAsync<{
|
|
54
145
|
proof: Uint8Array<ArrayBufferLike>;
|
|
55
146
|
contextualAlias: {
|
|
56
147
|
context: Uint8Array<ArrayBufferLike>;
|
|
@@ -60,7 +151,22 @@ export declare const createAccountsProvider: (transport?: Transport) => {
|
|
|
60
151
|
ringRevision: number;
|
|
61
152
|
}, import("@novasamatech/scale").CodecError<undefined, "CreateProofErr::Rejected"> | import("@novasamatech/scale").CodecError<{
|
|
62
153
|
reason: string;
|
|
63
|
-
}, "CreateProofErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "CreateProofErr::RingNotFound"> | import("@novasamatech/scale").CodecError<undefined, "CreateProofErr::NotMember">>;
|
|
154
|
+
}, "CreateProofErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "CreateProofErr::RingNotFound"> | import("@novasamatech/scale").CodecError<undefined, "CreateProofErr::NotMember"> | import("@novasamatech/scale").CodecError<undefined, "CreateProofErr::KeyNotRegistered"> | import("@novasamatech/scale").CodecError<undefined, "CreateProofErr::KeyNotInRing"> | import("@novasamatech/scale").CodecError<undefined, "CreateProofErr::NotAllowlisted">>;
|
|
155
|
+
/**
|
|
156
|
+
* Signs `message` with the ring VRF member key itself, producing an ordinary
|
|
157
|
+
* signature rather than an anonymous ring proof (RFC-0024).
|
|
158
|
+
*
|
|
159
|
+
* Takes no context and no ring: it derives no alias and proves no
|
|
160
|
+
* membership. A verifier needs the member public key, so the signature is
|
|
161
|
+
* **linkable** to every other use of that key — including every ring the key
|
|
162
|
+
* is a member of.
|
|
163
|
+
*
|
|
164
|
+
* Like `createRingVRFProof`, a foreign `keyHandle` requires the owner's
|
|
165
|
+
* manifest allowlist and has no prompt fallback.
|
|
166
|
+
*/
|
|
167
|
+
ringVrfSign(keyHandle: RingVrfKeyHandle, message: Uint8Array): import("neverthrow").ResultAsync<Uint8Array<ArrayBufferLike>, import("@novasamatech/scale").CodecError<undefined, "RingVrfSignErr::NotConnected"> | import("@novasamatech/scale").CodecError<undefined, "RingVrfSignErr::Rejected"> | import("@novasamatech/scale").CodecError<{
|
|
168
|
+
reason: string;
|
|
169
|
+
}, "RingVrfSignErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "RingVrfSignErr::KeyNotRegistered"> | import("@novasamatech/scale").CodecError<undefined, "RingVrfSignErr::NotAllowlisted">>;
|
|
64
170
|
/**
|
|
65
171
|
* Produces an sr25519 (schnorrkel) VRF signature from a product account (RFC-0023).
|
|
66
172
|
*
|
|
@@ -101,19 +207,87 @@ export declare const accounts: {
|
|
|
101
207
|
}, import("@novasamatech/scale").CodecError<undefined, "RequestCredentialsErr::NotConnected"> | import("@novasamatech/scale").CodecError<undefined, "RequestCredentialsErr::Rejected"> | import("@novasamatech/scale").CodecError<undefined, "RequestCredentialsErr::DomainNotValid"> | import("@novasamatech/scale").CodecError<{
|
|
102
208
|
reason: string;
|
|
103
209
|
}, "RequestCredentialsErr::Unknown">>;
|
|
104
|
-
|
|
210
|
+
/**
|
|
211
|
+
* Registers a ring VRF key this product owns, declaring the ring it is
|
|
212
|
+
* intended for (RFC-0024).
|
|
213
|
+
*
|
|
214
|
+
* Ownership is the calling product, never a parameter, so this is
|
|
215
|
+
* permissionless and prompt-free. Registering the same `index` for another
|
|
216
|
+
* `ring` extends the existing entry rather than creating a second one.
|
|
217
|
+
*
|
|
218
|
+
* Registration declares *intent*, not membership: it says "this is the key
|
|
219
|
+
* I will use for that ring", not "the user is a person". Membership is
|
|
220
|
+
* still discovered only by attempting a proof.
|
|
221
|
+
*/
|
|
222
|
+
registerRingVrfKey(index: AccountSelector, ring: CodecType<typeof RingLocation>): import("neverthrow").ResultAsync<Uint8Array<ArrayBufferLike>, import("@novasamatech/scale").CodecError<undefined, "RegisterRingVrfKeyErr::NotConnected"> | import("@novasamatech/scale").CodecError<undefined, "RegisterRingVrfKeyErr::Rejected"> | import("@novasamatech/scale").CodecError<{
|
|
223
|
+
reason: string;
|
|
224
|
+
}, "RegisterRingVrfKeyErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "RegisterRingVrfKeyErr::RingNotFound">>;
|
|
225
|
+
/**
|
|
226
|
+
* Lists the ring VRF registry entries owned by `owner` — this product or
|
|
227
|
+
* another one (RFC-0024).
|
|
228
|
+
*
|
|
229
|
+
* Listing your own keys is permissionless; a foreign `owner` needs a grant
|
|
230
|
+
* or produces a user prompt. `'PublicKey'` disclosure additionally returns
|
|
231
|
+
* the member public key, which is linkable across every ring it appears in
|
|
232
|
+
* and so is permissioned cross-product.
|
|
233
|
+
*
|
|
234
|
+
* Select the entry you want by the rings it declares, never by index.
|
|
235
|
+
*/
|
|
236
|
+
listRingVrfKeys(owner: string, disclosure?: RingVrfKeyDisclosure): import("neverthrow").ResultAsync<{
|
|
237
|
+
handle: [string, {
|
|
238
|
+
tag: "Index";
|
|
239
|
+
value: number;
|
|
240
|
+
} | {
|
|
241
|
+
tag: "Raw";
|
|
242
|
+
value: Uint8Array<ArrayBufferLike>;
|
|
243
|
+
}];
|
|
244
|
+
rings: {
|
|
245
|
+
chainId: `0x${string}`;
|
|
246
|
+
junctions: ({
|
|
247
|
+
tag: "PalletInstance";
|
|
248
|
+
value: number;
|
|
249
|
+
} | {
|
|
250
|
+
tag: "CollectionId";
|
|
251
|
+
value: Uint8Array<ArrayBufferLike>;
|
|
252
|
+
})[];
|
|
253
|
+
}[];
|
|
254
|
+
publicKey: Uint8Array<ArrayBufferLike> | undefined;
|
|
255
|
+
}[], import("@novasamatech/scale").CodecError<undefined, "ListRingVrfKeysErr::NotConnected"> | import("@novasamatech/scale").CodecError<undefined, "ListRingVrfKeysErr::Rejected"> | import("@novasamatech/scale").CodecError<{
|
|
256
|
+
reason: string;
|
|
257
|
+
}, "ListRingVrfKeysErr::Unknown">>;
|
|
258
|
+
/**
|
|
259
|
+
* Reads the contextual alias `keyHandle` resolves to under `context` in `ring`
|
|
260
|
+
* (RFC-0004, amended by RFC-0024).
|
|
261
|
+
*
|
|
262
|
+
* `ring` stays explicit even though the handle carries its declared rings,
|
|
263
|
+
* because a key may be registered for several and the caller must say which
|
|
264
|
+
* the alias is against; the host fails with `KeyNotInRing` otherwise.
|
|
265
|
+
*
|
|
266
|
+
* Check `ringRevision` on each use of the resulting alias and renew when it
|
|
267
|
+
* has moved — nothing else watches for it.
|
|
268
|
+
*/
|
|
269
|
+
getContextualAlias(keyHandle: RingVrfKeyHandle, context: ProofContext, ring: CodecType<typeof RingLocation>): import("neverthrow").ResultAsync<{
|
|
105
270
|
context: Uint8Array<ArrayBufferLike>;
|
|
106
271
|
alias: Uint8Array<ArrayBufferLike>;
|
|
107
272
|
}, import("@novasamatech/scale").CodecError<undefined, "GetAliasErr::Rejected"> | import("@novasamatech/scale").CodecError<{
|
|
108
273
|
reason: string;
|
|
109
|
-
}, "GetAliasErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "GetAliasErr::RingNotFound"> | import("@novasamatech/scale").CodecError<undefined, "GetAliasErr::NotMember">>;
|
|
274
|
+
}, "GetAliasErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "GetAliasErr::RingNotFound"> | import("@novasamatech/scale").CodecError<undefined, "GetAliasErr::NotMember"> | import("@novasamatech/scale").CodecError<undefined, "GetAliasErr::KeyNotRegistered"> | import("@novasamatech/scale").CodecError<undefined, "GetAliasErr::KeyNotInRing">>;
|
|
110
275
|
getLegacyAccounts(): import("neverthrow").ResultAsync<{
|
|
111
276
|
publicKey: Uint8Array<ArrayBufferLike>;
|
|
112
277
|
name: string | undefined;
|
|
113
278
|
}[], import("@novasamatech/scale").CodecError<undefined, "RequestCredentialsErr::NotConnected"> | import("@novasamatech/scale").CodecError<undefined, "RequestCredentialsErr::Rejected"> | import("@novasamatech/scale").CodecError<undefined, "RequestCredentialsErr::DomainNotValid"> | import("@novasamatech/scale").CodecError<{
|
|
114
279
|
reason: string;
|
|
115
280
|
}, "RequestCredentialsErr::Unknown">>;
|
|
116
|
-
|
|
281
|
+
/**
|
|
282
|
+
* Produces an anonymous ring VRF proof with `keyHandle` under `context` in
|
|
283
|
+
* `ring` (RFC-0004, amended by RFC-0024).
|
|
284
|
+
*
|
|
285
|
+
* A proof is a bearer token for its context's alias, so a *foreign*
|
|
286
|
+
* `keyHandle` is admitted only when the owning product allowlisted this one
|
|
287
|
+
* in its manifest — there is no user-prompt fallback, and the host returns
|
|
288
|
+
* `NotAllowlisted` otherwise.
|
|
289
|
+
*/
|
|
290
|
+
createRingVRFProof(keyHandle: RingVrfKeyHandle, context: ProofContext, ring: CodecType<typeof RingLocation>, message: Uint8Array): import("neverthrow").ResultAsync<{
|
|
117
291
|
proof: Uint8Array<ArrayBufferLike>;
|
|
118
292
|
contextualAlias: {
|
|
119
293
|
context: Uint8Array<ArrayBufferLike>;
|
|
@@ -123,7 +297,22 @@ export declare const accounts: {
|
|
|
123
297
|
ringRevision: number;
|
|
124
298
|
}, import("@novasamatech/scale").CodecError<undefined, "CreateProofErr::Rejected"> | import("@novasamatech/scale").CodecError<{
|
|
125
299
|
reason: string;
|
|
126
|
-
}, "CreateProofErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "CreateProofErr::RingNotFound"> | import("@novasamatech/scale").CodecError<undefined, "CreateProofErr::NotMember">>;
|
|
300
|
+
}, "CreateProofErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "CreateProofErr::RingNotFound"> | import("@novasamatech/scale").CodecError<undefined, "CreateProofErr::NotMember"> | import("@novasamatech/scale").CodecError<undefined, "CreateProofErr::KeyNotRegistered"> | import("@novasamatech/scale").CodecError<undefined, "CreateProofErr::KeyNotInRing"> | import("@novasamatech/scale").CodecError<undefined, "CreateProofErr::NotAllowlisted">>;
|
|
301
|
+
/**
|
|
302
|
+
* Signs `message` with the ring VRF member key itself, producing an ordinary
|
|
303
|
+
* signature rather than an anonymous ring proof (RFC-0024).
|
|
304
|
+
*
|
|
305
|
+
* Takes no context and no ring: it derives no alias and proves no
|
|
306
|
+
* membership. A verifier needs the member public key, so the signature is
|
|
307
|
+
* **linkable** to every other use of that key — including every ring the key
|
|
308
|
+
* is a member of.
|
|
309
|
+
*
|
|
310
|
+
* Like `createRingVRFProof`, a foreign `keyHandle` requires the owner's
|
|
311
|
+
* manifest allowlist and has no prompt fallback.
|
|
312
|
+
*/
|
|
313
|
+
ringVrfSign(keyHandle: RingVrfKeyHandle, message: Uint8Array): import("neverthrow").ResultAsync<Uint8Array<ArrayBufferLike>, import("@novasamatech/scale").CodecError<undefined, "RingVrfSignErr::NotConnected"> | import("@novasamatech/scale").CodecError<undefined, "RingVrfSignErr::Rejected"> | import("@novasamatech/scale").CodecError<{
|
|
314
|
+
reason: string;
|
|
315
|
+
}, "RingVrfSignErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "RingVrfSignErr::KeyNotRegistered"> | import("@novasamatech/scale").CodecError<undefined, "RingVrfSignErr::NotAllowlisted">>;
|
|
127
316
|
/**
|
|
128
317
|
* Produces an sr25519 (schnorrkel) VRF signature from a product account (RFC-0023).
|
|
129
318
|
*
|
package/dist/accounts.js
CHANGED
|
@@ -1,9 +1,19 @@
|
|
|
1
|
-
import { CreateProofErr, GetAliasErr, GetUserIdErr, LoginErr, ProductProofContext, RequestCredentialsErr, RingLocation, SignVrfErr, SigningPayload, SigningPayloadWithoutAccount, SigningRawPayload, SigningRawPayloadWithoutAccount, assertEnumVariant, createHostApi, derivationIndexOf, enumValue, fromHex, isEnumVariant, toHex, } from '@novasamatech/host-api';
|
|
1
|
+
import { CreateProofErr, GetAliasErr, GetUserIdErr, ListRingVrfKeysErr, LoginErr, ProductProofContext, RegisterRingVrfKeyErr, RequestCredentialsErr, RingLocation, RingVrfSignErr, SignVrfErr, SigningPayload, SigningPayloadWithoutAccount, SigningRawPayload, SigningRawPayloadWithoutAccount, assertEnumVariant, createHostApi, derivationIndexOf, enumValue, fromHex, isEnumVariant, toHex, } from '@novasamatech/host-api';
|
|
2
2
|
import { decAnyMetadata, unifyMetadata } from '@polkadot-api/substrate-bindings';
|
|
3
3
|
import { err, ok } from 'neverthrow';
|
|
4
4
|
import { AccountId } from 'polkadot-api';
|
|
5
5
|
import { getPolkadotSignerFromPjs } from 'polkadot-api/pjs-signer';
|
|
6
6
|
import { sandboxTransport } from './sandboxTransport.js';
|
|
7
|
+
/**
|
|
8
|
+
* Builds a {@link RingVrfKeyHandle} for a key inside `owner`'s ring VRF domain.
|
|
9
|
+
*
|
|
10
|
+
* Intended for naming your *own* keys — the ones you passed to
|
|
11
|
+
* `registerRingVrfKey`. For a foreign key, take the handle from
|
|
12
|
+
* `listRingVrfKeys` instead of constructing one.
|
|
13
|
+
*/
|
|
14
|
+
export function ringVrfKeyHandle(owner, index) {
|
|
15
|
+
return [owner, derivationIndexOf(index)];
|
|
16
|
+
}
|
|
7
17
|
const UNSUPPORTED_VERSION_ERROR = 'Unsupported message version';
|
|
8
18
|
export const createAccountsProvider = (transport = sandboxTransport) => {
|
|
9
19
|
const hostApi = createHostApi(transport);
|
|
@@ -48,9 +58,67 @@ export const createAccountsProvider = (transport = sandboxTransport) => {
|
|
|
48
58
|
return err(new RequestCredentialsErr.Unknown({ reason: `Unsupported response version ${response.tag}` }));
|
|
49
59
|
});
|
|
50
60
|
},
|
|
51
|
-
|
|
61
|
+
/**
|
|
62
|
+
* Registers a ring VRF key this product owns, declaring the ring it is
|
|
63
|
+
* intended for (RFC-0024).
|
|
64
|
+
*
|
|
65
|
+
* Ownership is the calling product, never a parameter, so this is
|
|
66
|
+
* permissionless and prompt-free. Registering the same `index` for another
|
|
67
|
+
* `ring` extends the existing entry rather than creating a second one.
|
|
68
|
+
*
|
|
69
|
+
* Registration declares *intent*, not membership: it says "this is the key
|
|
70
|
+
* I will use for that ring", not "the user is a person". Membership is
|
|
71
|
+
* still discovered only by attempting a proof.
|
|
72
|
+
*/
|
|
73
|
+
registerRingVrfKey(index, ring) {
|
|
74
|
+
return hostApi
|
|
75
|
+
.accountRegisterRingVrfKey(enumValue('v1', [derivationIndexOf(index), ring]))
|
|
76
|
+
.mapErr(e => e.value)
|
|
77
|
+
.andThen(response => {
|
|
78
|
+
if (isEnumVariant(response, 'v1')) {
|
|
79
|
+
return ok(response.value);
|
|
80
|
+
}
|
|
81
|
+
// @ts-expect-error response.tag is never here
|
|
82
|
+
return err(new RegisterRingVrfKeyErr.Unknown({ reason: `Unsupported response version ${response.tag}` }));
|
|
83
|
+
});
|
|
84
|
+
},
|
|
85
|
+
/**
|
|
86
|
+
* Lists the ring VRF registry entries owned by `owner` — this product or
|
|
87
|
+
* another one (RFC-0024).
|
|
88
|
+
*
|
|
89
|
+
* Listing your own keys is permissionless; a foreign `owner` needs a grant
|
|
90
|
+
* or produces a user prompt. `'PublicKey'` disclosure additionally returns
|
|
91
|
+
* the member public key, which is linkable across every ring it appears in
|
|
92
|
+
* and so is permissioned cross-product.
|
|
93
|
+
*
|
|
94
|
+
* Select the entry you want by the rings it declares, never by index.
|
|
95
|
+
*/
|
|
96
|
+
listRingVrfKeys(owner, disclosure = 'Anonymized') {
|
|
52
97
|
return hostApi
|
|
53
|
-
.
|
|
98
|
+
.accountListRingVrfKeys(enumValue('v1', [owner, disclosure]))
|
|
99
|
+
.mapErr(e => e.value)
|
|
100
|
+
.andThen(response => {
|
|
101
|
+
if (isEnumVariant(response, 'v1')) {
|
|
102
|
+
return ok(response.value);
|
|
103
|
+
}
|
|
104
|
+
// @ts-expect-error response.tag is never here
|
|
105
|
+
return err(new ListRingVrfKeysErr.Unknown({ reason: `Unsupported response version ${response.tag}` }));
|
|
106
|
+
});
|
|
107
|
+
},
|
|
108
|
+
/**
|
|
109
|
+
* Reads the contextual alias `keyHandle` resolves to under `context` in `ring`
|
|
110
|
+
* (RFC-0004, amended by RFC-0024).
|
|
111
|
+
*
|
|
112
|
+
* `ring` stays explicit even though the handle carries its declared rings,
|
|
113
|
+
* because a key may be registered for several and the caller must say which
|
|
114
|
+
* the alias is against; the host fails with `KeyNotInRing` otherwise.
|
|
115
|
+
*
|
|
116
|
+
* Check `ringRevision` on each use of the resulting alias and renew when it
|
|
117
|
+
* has moved — nothing else watches for it.
|
|
118
|
+
*/
|
|
119
|
+
getContextualAlias(keyHandle, context, ring) {
|
|
120
|
+
return hostApi
|
|
121
|
+
.accountGetAlias(enumValue('v1', [keyHandle, toProofContext(context), ring]))
|
|
54
122
|
.mapErr(e => e.value)
|
|
55
123
|
.andThen(response => {
|
|
56
124
|
if (isEnumVariant(response, 'v1')) {
|
|
@@ -72,9 +140,18 @@ export const createAccountsProvider = (transport = sandboxTransport) => {
|
|
|
72
140
|
return err(new RequestCredentialsErr.Unknown({ reason: `Unsupported response version ${response.tag}` }));
|
|
73
141
|
});
|
|
74
142
|
},
|
|
75
|
-
|
|
143
|
+
/**
|
|
144
|
+
* Produces an anonymous ring VRF proof with `keyHandle` under `context` in
|
|
145
|
+
* `ring` (RFC-0004, amended by RFC-0024).
|
|
146
|
+
*
|
|
147
|
+
* A proof is a bearer token for its context's alias, so a *foreign*
|
|
148
|
+
* `keyHandle` is admitted only when the owning product allowlisted this one
|
|
149
|
+
* in its manifest — there is no user-prompt fallback, and the host returns
|
|
150
|
+
* `NotAllowlisted` otherwise.
|
|
151
|
+
*/
|
|
152
|
+
createRingVRFProof(keyHandle, context, ring, message) {
|
|
76
153
|
return hostApi
|
|
77
|
-
.accountCreateProof(enumValue('v1', [toProofContext(context), ring, message]))
|
|
154
|
+
.accountCreateProof(enumValue('v1', [keyHandle, toProofContext(context), ring, message]))
|
|
78
155
|
.mapErr(e => e.value)
|
|
79
156
|
.andThen(response => {
|
|
80
157
|
if (isEnumVariant(response, 'v1')) {
|
|
@@ -84,6 +161,30 @@ export const createAccountsProvider = (transport = sandboxTransport) => {
|
|
|
84
161
|
return err(new CreateProofErr.Unknown({ reason: `Unsupported response version ${response.tag}` }));
|
|
85
162
|
});
|
|
86
163
|
},
|
|
164
|
+
/**
|
|
165
|
+
* Signs `message` with the ring VRF member key itself, producing an ordinary
|
|
166
|
+
* signature rather than an anonymous ring proof (RFC-0024).
|
|
167
|
+
*
|
|
168
|
+
* Takes no context and no ring: it derives no alias and proves no
|
|
169
|
+
* membership. A verifier needs the member public key, so the signature is
|
|
170
|
+
* **linkable** to every other use of that key — including every ring the key
|
|
171
|
+
* is a member of.
|
|
172
|
+
*
|
|
173
|
+
* Like `createRingVRFProof`, a foreign `keyHandle` requires the owner's
|
|
174
|
+
* manifest allowlist and has no prompt fallback.
|
|
175
|
+
*/
|
|
176
|
+
ringVrfSign(keyHandle, message) {
|
|
177
|
+
return hostApi
|
|
178
|
+
.accountRingVrfSign(enumValue('v1', [keyHandle, message]))
|
|
179
|
+
.mapErr(e => e.value)
|
|
180
|
+
.andThen(response => {
|
|
181
|
+
if (isEnumVariant(response, 'v1')) {
|
|
182
|
+
return ok(response.value);
|
|
183
|
+
}
|
|
184
|
+
// @ts-expect-error response.tag is never here
|
|
185
|
+
return err(new RingVrfSignErr.Unknown({ reason: `Unsupported response version ${response.tag}` }));
|
|
186
|
+
});
|
|
187
|
+
},
|
|
87
188
|
/**
|
|
88
189
|
* Produces an sr25519 (schnorrkel) VRF signature from a product account (RFC-0023).
|
|
89
190
|
*
|
package/dist/index.d.ts
CHANGED
|
@@ -8,8 +8,8 @@ export type { ChatBotRegistrationResult, ChatCustomMessageRenderer, ChatCustomMe
|
|
|
8
8
|
export { createProductChatManager, matchChatCustomRenderers } from './chat.js';
|
|
9
9
|
export type { ProductAccountId, ProductAccountRef, SignedStatement, Statement, StatementTopicFilter, StatementsPage, Topic, } from './statementStore.js';
|
|
10
10
|
export { createStatementStore } from './statementStore.js';
|
|
11
|
-
export type { AccountConnectionStatus, AccountSelector, LegacyAccount, ProductAccount, ProofContext, VrfTranscriptItem, } from './accounts.js';
|
|
12
|
-
export { accounts, createAccountsProvider } from './accounts.js';
|
|
11
|
+
export type { AccountConnectionStatus, AccountSelector, LegacyAccount, ProductAccount, ProofContext, RegisteredRingVrfKey, RingVrfKeyDisclosure, RingVrfKeyHandle, VrfTranscriptItem, } from './accounts.js';
|
|
12
|
+
export { accounts, createAccountsProvider, ringVrfKeyHandle } from './accounts.js';
|
|
13
13
|
export type { ThemeMode } from './theme.js';
|
|
14
14
|
export { createThemeProvider } from './theme.js';
|
|
15
15
|
export { createLocalStorage, hostLocalStorage } from './localStorage.js';
|
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@ export { createLegacyExtensionEnableFactory, injectSpektrExtension } from './inj
|
|
|
6
6
|
export { createPapiProvider } from './papiProvider.js';
|
|
7
7
|
export { createProductChatManager, matchChatCustomRenderers } from './chat.js';
|
|
8
8
|
export { createStatementStore } from './statementStore.js';
|
|
9
|
-
export { accounts, createAccountsProvider } from './accounts.js';
|
|
9
|
+
export { accounts, createAccountsProvider, ringVrfKeyHandle } from './accounts.js';
|
|
10
10
|
export { createThemeProvider } from './theme.js';
|
|
11
11
|
export { createLocalStorage, hostLocalStorage } from './localStorage.js';
|
|
12
12
|
export { createNotificationManager, notificationManager } from './notification.js';
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@novasamatech/host-api-wrapper",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.9.
|
|
4
|
+
"version": "0.9.3",
|
|
5
5
|
"description": "Host API wrapper: integrate and run your product inside Polkadot browser.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
7
|
"repository": {
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"@polkadot/extension-inject": "^0.63.1",
|
|
29
29
|
"@polkadot-api/json-rpc-provider-proxy": "^0.4.0",
|
|
30
30
|
"@polkadot-api/substrate-bindings": "^0.20.3",
|
|
31
|
-
"@novasamatech/host-api": "0.9.
|
|
31
|
+
"@novasamatech/host-api": "0.9.3",
|
|
32
32
|
"polkadot-api": ">=2",
|
|
33
33
|
"neverthrow": "^8.2.0"
|
|
34
34
|
},
|