@novasamatech/host-papp 0.8.10 → 0.8.11

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 CHANGED
@@ -200,6 +200,31 @@ await currentSession.signRaw({
200
200
  });
201
201
  ```
202
202
 
203
+ ## Ring VRF proofs and aliases
204
+
205
+ A `UserSession` can ask the paired device for a privacy-preserving contextual alias, or a
206
+ ring VRF proof, for a product-scoped `context` and a `ring` location. The device
207
+ selects the member key for the ring; `callingProductId` names the product the host is acting
208
+ for. Both take the same `(context, ring)` so the alias in the proof matches `getRingVrfAlias`.
209
+
210
+ ```ts
211
+ const context = ['product.dot', '0x00']; // [productId, suffix] — suffix is a 0x-hex string
212
+ const ring = {
213
+ chainId: '0x…', // 32-byte chain genesis hash
214
+ junctions: [{ tag: 'PalletInstance', value: 42 }],
215
+ };
216
+
217
+ const alias = await currentSession.getRingVrfAlias('caller.dot', context, ring);
218
+
219
+ const proof = await currentSession.createRingVrfProof('caller.dot', context, ring, new Uint8Array([0x48, 0x69]));
220
+ proof.match(
221
+ ({ proof, contextualAlias, ringIndex, ringRevision }) =>
222
+ console.log('proof at ring', ringIndex, 'revision', ringRevision),
223
+ // failures decode to a structured `RingVrfError` (RingNotFound / NotMember / Rejected / Unknown)
224
+ error => console.error('proof failed:', error),
225
+ );
226
+ ```
227
+
203
228
  ## Identity lookups
204
229
 
205
230
  `papp.identity` resolves on-chain identity data (lite / full username, credibility, slots)
package/dist/index.d.ts CHANGED
@@ -10,5 +10,5 @@ export type { UserSession } from './sso/sessionManager/userSession.js';
10
10
  export type { StoredUserSession } from './sso/userSessionRepository.js';
11
11
  export type { Identity } from './identity/types.js';
12
12
  export type { SignRawLegacyRequest, SignRawLegacyResponse, SigningPayloadRequest, SigningPayloadResponse, SigningRawRequest, SigningRequest, } from './sso/sessionManager/scale/signing.js';
13
- export type { RingVrfAliasRequest, RingVrfAliasResponse } from './sso/sessionManager/scale/ringVrf.js';
13
+ export type { RingVrfAliasRequest, RingVrfAliasResponse, RingVrfProofRequest, RingVrfProofResponse, } from './sso/sessionManager/scale/ringVrf.js';
14
14
  export type { CreateTransactionLegacyRequest, CreateTransactionRequest, CreateTransactionResponse, } from './sso/sessionManager/scale/createTransaction.js';
@@ -54,8 +54,18 @@ export declare const RemoteMessageCodec: import("scale-ts").Codec<{
54
54
  } | {
55
55
  tag: "RingVrfAliasRequest";
56
56
  value: {
57
- productAccountId: [string, number];
58
- productId: string;
57
+ callingProductId: string;
58
+ context: [string, `0x${string}`];
59
+ ring: {
60
+ chainId: `0x${string}`;
61
+ junctions: ({
62
+ tag: "PalletInstance";
63
+ value: number;
64
+ } | {
65
+ tag: "CollectionId";
66
+ value: Uint8Array<ArrayBufferLike>;
67
+ })[];
68
+ };
59
69
  };
60
70
  } | {
61
71
  tag: "RingVrfAliasResponse";
@@ -64,7 +74,9 @@ export declare const RemoteMessageCodec: import("scale-ts").Codec<{
64
74
  payload: import("scale-ts").ResultPayload<{
65
75
  context: Uint8Array<ArrayBufferLike>;
66
76
  alias: Uint8Array<ArrayBufferLike>;
67
- }, string>;
77
+ }, import("@novasamatech/scale").CodecError<undefined, "RingVrfError::Rejected"> | import("@novasamatech/scale").CodecError<{
78
+ reason: string;
79
+ }, "RingVrfError::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "RingVrfError::RingNotFound"> | import("@novasamatech/scale").CodecError<undefined, "RingVrfError::NotMember">>;
68
80
  };
69
81
  } | {
70
82
  tag: "ResourceAllocationRequest";
@@ -179,6 +191,39 @@ export declare const RemoteMessageCodec: import("scale-ts").Codec<{
179
191
  respondingTo: string;
180
192
  signature: import("scale-ts").ResultPayload<Uint8Array<ArrayBufferLike>, string>;
181
193
  };
194
+ } | {
195
+ tag: "RingVrfProofRequest";
196
+ value: {
197
+ callingProductId: string;
198
+ context: [string, `0x${string}`];
199
+ ring: {
200
+ chainId: `0x${string}`;
201
+ junctions: ({
202
+ tag: "PalletInstance";
203
+ value: number;
204
+ } | {
205
+ tag: "CollectionId";
206
+ value: Uint8Array<ArrayBufferLike>;
207
+ })[];
208
+ };
209
+ message: Uint8Array<ArrayBufferLike>;
210
+ };
211
+ } | {
212
+ tag: "RingVrfProofResponse";
213
+ value: {
214
+ respondingTo: string;
215
+ payload: import("scale-ts").ResultPayload<{
216
+ proof: Uint8Array<ArrayBufferLike>;
217
+ contextualAlias: {
218
+ context: Uint8Array<ArrayBufferLike>;
219
+ alias: Uint8Array<ArrayBufferLike>;
220
+ };
221
+ ringIndex: number;
222
+ ringRevision: number;
223
+ }, import("@novasamatech/scale").CodecError<undefined, "RingVrfError::Rejected"> | import("@novasamatech/scale").CodecError<{
224
+ reason: string;
225
+ }, "RingVrfError::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "RingVrfError::RingNotFound"> | import("@novasamatech/scale").CodecError<undefined, "RingVrfError::NotMember">>;
226
+ };
182
227
  };
183
228
  };
184
229
  }>;
@@ -1,7 +1,7 @@
1
1
  import { Enum, Struct, _void, str } from 'scale-ts';
2
2
  import { CreateTransactionLegacyRequestCodec, CreateTransactionRequestCodec, CreateTransactionResponseCodec, } from './createTransaction.js';
3
3
  import { ResourceAllocationRequestCodec, ResourceAllocationResponseCodec } from './resourceAllocation.js';
4
- import { RingVrfAliasRequestCodec, RingVrfAliasResponseCodec } from './ringVrf.js';
4
+ import { RingVrfAliasRequestCodec, RingVrfAliasResponseCodec, RingVrfProofRequestCodec, RingVrfProofResponseCodec, } from './ringVrf.js';
5
5
  import { SignRawLegacyRequestCodec, SignRawLegacyResponseCodec, SigningRequestCodec, SigningResponseCodec, } from './signing.js';
6
6
  export const RemoteMessageCodec = Struct({
7
7
  messageId: str,
@@ -19,6 +19,8 @@ export const RemoteMessageCodec = Struct({
19
19
  CreateTransactionLegacyRequest: CreateTransactionLegacyRequestCodec,
20
20
  SignRawLegacyRequest: SignRawLegacyRequestCodec,
21
21
  SignRawLegacyResponse: SignRawLegacyResponseCodec,
22
+ RingVrfProofRequest: RingVrfProofRequestCodec,
23
+ RingVrfProofResponse: RingVrfProofResponseCodec,
22
24
  }),
23
25
  }),
24
26
  });
@@ -1,8 +1,41 @@
1
1
  import type { CodecType } from 'scale-ts';
2
+ export declare const RingVrfError: [import("scale-ts").Encoder<import("@novasamatech/scale").CodecError<undefined, "RingVrfError::Rejected"> | import("@novasamatech/scale").CodecError<{
3
+ reason: string;
4
+ }, "RingVrfError::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "RingVrfError::RingNotFound"> | import("@novasamatech/scale").CodecError<undefined, "RingVrfError::NotMember">>, import("scale-ts").Decoder<import("@novasamatech/scale").CodecError<undefined, "RingVrfError::Rejected"> | import("@novasamatech/scale").CodecError<{
5
+ reason: string;
6
+ }, "RingVrfError::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "RingVrfError::RingNotFound"> | import("@novasamatech/scale").CodecError<undefined, "RingVrfError::NotMember">>] & {
7
+ enc: import("scale-ts").Encoder<import("@novasamatech/scale").CodecError<undefined, "RingVrfError::Rejected"> | import("@novasamatech/scale").CodecError<{
8
+ reason: string;
9
+ }, "RingVrfError::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "RingVrfError::RingNotFound"> | import("@novasamatech/scale").CodecError<undefined, "RingVrfError::NotMember">>;
10
+ dec: import("scale-ts").Decoder<import("@novasamatech/scale").CodecError<undefined, "RingVrfError::Rejected"> | import("@novasamatech/scale").CodecError<{
11
+ reason: string;
12
+ }, "RingVrfError::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "RingVrfError::RingNotFound"> | import("@novasamatech/scale").CodecError<undefined, "RingVrfError::NotMember">>;
13
+ } & {
14
+ readonly RingNotFound: import("@novasamatech/scale").ErrCodec<undefined, "RingVrfError::RingNotFound">;
15
+ readonly NotMember: import("@novasamatech/scale").ErrCodec<undefined, "RingVrfError::NotMember">;
16
+ readonly Rejected: import("@novasamatech/scale").ErrCodec<undefined, "RingVrfError::Rejected">;
17
+ readonly Unknown: import("@novasamatech/scale").ErrCodec<{
18
+ reason: string;
19
+ }, "RingVrfError::Unknown">;
20
+ } & {
21
+ [Symbol.hasInstance](v: unknown): v is import("@novasamatech/scale").CodecError<undefined, "RingVrfError::Rejected"> | import("@novasamatech/scale").CodecError<{
22
+ reason: string;
23
+ }, "RingVrfError::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "RingVrfError::RingNotFound"> | import("@novasamatech/scale").CodecError<undefined, "RingVrfError::NotMember">;
24
+ };
2
25
  export type RingVrfAliasRequest = CodecType<typeof RingVrfAliasRequestCodec>;
3
26
  export declare const RingVrfAliasRequestCodec: import("scale-ts").Codec<{
4
- productAccountId: [string, number];
5
- productId: string;
27
+ callingProductId: string;
28
+ context: [string, `0x${string}`];
29
+ ring: {
30
+ chainId: `0x${string}`;
31
+ junctions: ({
32
+ tag: "PalletInstance";
33
+ value: number;
34
+ } | {
35
+ tag: "CollectionId";
36
+ value: Uint8Array<ArrayBufferLike>;
37
+ })[];
38
+ };
6
39
  }>;
7
40
  export type RingVrfAliasResponse = CodecType<typeof RingVrfAliasResponseCodec>;
8
41
  export declare const RingVrfAliasResponseCodec: import("scale-ts").Codec<{
@@ -10,5 +43,38 @@ export declare const RingVrfAliasResponseCodec: import("scale-ts").Codec<{
10
43
  payload: import("scale-ts").ResultPayload<{
11
44
  context: Uint8Array<ArrayBufferLike>;
12
45
  alias: Uint8Array<ArrayBufferLike>;
13
- }, string>;
46
+ }, import("@novasamatech/scale").CodecError<undefined, "RingVrfError::Rejected"> | import("@novasamatech/scale").CodecError<{
47
+ reason: string;
48
+ }, "RingVrfError::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "RingVrfError::RingNotFound"> | import("@novasamatech/scale").CodecError<undefined, "RingVrfError::NotMember">>;
49
+ }>;
50
+ export type RingVrfProofRequest = CodecType<typeof RingVrfProofRequestCodec>;
51
+ export declare const RingVrfProofRequestCodec: import("scale-ts").Codec<{
52
+ callingProductId: string;
53
+ context: [string, `0x${string}`];
54
+ ring: {
55
+ chainId: `0x${string}`;
56
+ junctions: ({
57
+ tag: "PalletInstance";
58
+ value: number;
59
+ } | {
60
+ tag: "CollectionId";
61
+ value: Uint8Array<ArrayBufferLike>;
62
+ })[];
63
+ };
64
+ message: Uint8Array<ArrayBufferLike>;
65
+ }>;
66
+ export type RingVrfProofResponse = CodecType<typeof RingVrfProofResponseCodec>;
67
+ export declare const RingVrfProofResponseCodec: import("scale-ts").Codec<{
68
+ respondingTo: string;
69
+ payload: import("scale-ts").ResultPayload<{
70
+ proof: Uint8Array<ArrayBufferLike>;
71
+ contextualAlias: {
72
+ context: Uint8Array<ArrayBufferLike>;
73
+ alias: Uint8Array<ArrayBufferLike>;
74
+ };
75
+ ringIndex: number;
76
+ ringRevision: number;
77
+ }, import("@novasamatech/scale").CodecError<undefined, "RingVrfError::Rejected"> | import("@novasamatech/scale").CodecError<{
78
+ reason: string;
79
+ }, "RingVrfError::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "RingVrfError::RingNotFound"> | import("@novasamatech/scale").CodecError<undefined, "RingVrfError::NotMember">>;
14
80
  }>;
@@ -1,10 +1,30 @@
1
- import { ContextualAlias, ProductAccountId } from '@novasamatech/host-api';
2
- import { Result, Struct, str } from 'scale-ts';
1
+ import { ContextualAlias, DotNsIdentifier, ProductProofContext, RingLocation, RingVrfProof, } from '@novasamatech/host-api';
2
+ import { ErrEnum } from '@novasamatech/scale';
3
+ import { Bytes, Result, Struct, _void, str } from 'scale-ts';
4
+ // Shared failure set for both alias and proof requests over the SSO channel;
5
+ // mirrors the Account Holder's `RingVrfError` (RFC 0004).
6
+ export const RingVrfError = ErrEnum('RingVrfError', {
7
+ RingNotFound: [_void, 'RingVrf: ring not found'],
8
+ NotMember: [_void, 'RingVrf: selected member key is not a member of the ring'],
9
+ Rejected: [_void, 'RingVrf: rejected'],
10
+ Unknown: [Struct({ reason: str }), 'RingVrf: unknown error'],
11
+ });
3
12
  export const RingVrfAliasRequestCodec = Struct({
4
- productAccountId: ProductAccountId,
5
- productId: str,
13
+ callingProductId: DotNsIdentifier,
14
+ context: ProductProofContext,
15
+ ring: RingLocation,
6
16
  });
7
17
  export const RingVrfAliasResponseCodec = Struct({
8
18
  respondingTo: str,
9
- payload: Result(ContextualAlias, str),
19
+ payload: Result(ContextualAlias, RingVrfError),
20
+ });
21
+ export const RingVrfProofRequestCodec = Struct({
22
+ callingProductId: DotNsIdentifier,
23
+ context: ProductProofContext,
24
+ ring: RingLocation,
25
+ message: Bytes(),
26
+ });
27
+ export const RingVrfProofResponseCodec = Struct({
28
+ respondingTo: str,
29
+ payload: Result(RingVrfProof, RingVrfError),
10
30
  });
@@ -1,4 +1,4 @@
1
- import { ContextualAlias, ProductAccountId } from '@novasamatech/host-api';
1
+ import { ContextualAlias, ProductProofContext, RingLocation, RingVrfProof } from '@novasamatech/host-api';
2
2
  import type { Encryption, StatementProver, StatementStoreAdapter } from '@novasamatech/statement-store';
3
3
  import type { StorageAdapter } from '@novasamatech/storage-adapter';
4
4
  import { ResultAsync } from 'neverthrow';
@@ -17,7 +17,8 @@ export type UserSession = StoredUserSession & {
17
17
  signRawLegacy(payload: SignRawLegacyRequest): ResultAsync<Uint8Array, Error>;
18
18
  createTransaction(payload: CreateTransactionRequest): ResultAsync<Uint8Array, Error>;
19
19
  createTransactionLegacy(payload: CreateTransactionLegacyRequest): ResultAsync<Uint8Array, Error>;
20
- getRingVrfAlias(productAccountId: CodecType<typeof ProductAccountId>, productId: string): ResultAsync<CodecType<typeof ContextualAlias>, Error>;
20
+ getRingVrfAlias(callingProductId: string, context: CodecType<typeof ProductProofContext>, ring: CodecType<typeof RingLocation>): ResultAsync<CodecType<typeof ContextualAlias>, Error>;
21
+ createRingVrfProof(callingProductId: string, context: CodecType<typeof ProductProofContext>, ring: CodecType<typeof RingLocation>, message: Uint8Array): ResultAsync<CodecType<typeof RingVrfProof>, Error>;
21
22
  requestResourceAllocation(request: ResourceAllocationRequest): ResultAsync<ApAllocationOutcome[], Error>;
22
23
  subscribe(callback: Callback<CodecType<typeof RemoteMessageCodec>, ResultAsync<boolean, Error>>): VoidFunction;
23
24
  dispose(): void;
@@ -1,4 +1,4 @@
1
- import { ContextualAlias, ProductAccountId } from '@novasamatech/host-api';
1
+ import { ContextualAlias, ProductProofContext, RingLocation, RingVrfProof } from '@novasamatech/host-api';
2
2
  import { enumValue } from '@novasamatech/scale';
3
3
  import { createSession } from '@novasamatech/statement-store';
4
4
  import { fieldListView } from '@novasamatech/storage-adapter';
@@ -237,12 +237,13 @@ export function createUserSession({ userSession, statementStore, encryption, sto
237
237
  return withHostActionTrace(withQueueTimeout(inner, 'createTransactionLegacy'), messageId, userSession.id);
238
238
  });
239
239
  },
240
- getRingVrfAlias(productAccountId, productId) {
240
+ getRingVrfAlias(callingProductId, context, ring) {
241
241
  return enqueue(() => {
242
242
  const messageId = nanoid();
243
243
  const data = enumValue('v1', enumValue('RingVrfAliasRequest', {
244
- productAccountId,
245
- productId,
244
+ callingProductId,
245
+ context,
246
+ ring,
246
247
  }));
247
248
  emitHostAction(messageId, actionKindFromMessageData(data), userSession.id);
248
249
  const responseFilter = (message) => {
@@ -254,7 +255,29 @@ export function createUserSession({ userSession, statementStore, encryption, sto
254
255
  };
255
256
  const request = session.request(RemoteMessageCodec, { messageId, data });
256
257
  const reply = session.waitForRequestMessage(RemoteMessageCodec, responseFilter);
257
- return withHostActionTrace(awaitReplyOrAckFailure(request, reply).andThen(result => result.success ? ok(result.value) : err(new Error(result.value))), messageId, userSession.id);
258
+ return withHostActionTrace(awaitReplyOrAckFailure(request, reply).andThen(result => result.success ? ok(result.value) : err(result.value)), messageId, userSession.id);
259
+ });
260
+ },
261
+ createRingVrfProof(callingProductId, context, ring, message) {
262
+ return enqueue(() => {
263
+ const messageId = nanoid();
264
+ const data = enumValue('v1', enumValue('RingVrfProofRequest', {
265
+ callingProductId,
266
+ context,
267
+ ring,
268
+ message,
269
+ }));
270
+ emitHostAction(messageId, actionKindFromMessageData(data), userSession.id);
271
+ const responseFilter = (incoming) => {
272
+ if (incoming.data.tag === 'v1' &&
273
+ incoming.data.value.tag === 'RingVrfProofResponse' &&
274
+ incoming.data.value.value.respondingTo === messageId) {
275
+ return incoming.data.value.value.payload;
276
+ }
277
+ };
278
+ const request = session.request(RemoteMessageCodec, { messageId, data });
279
+ const reply = session.waitForRequestMessage(RemoteMessageCodec, responseFilter);
280
+ return withHostActionTrace(awaitReplyOrAckFailure(request, reply).andThen(result => result.success ? ok(result.value) : err(result.value)), messageId, userSession.id);
258
281
  });
259
282
  },
260
283
  requestResourceAllocation(payload) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@novasamatech/host-papp",
3
3
  "type": "module",
4
- "version": "0.8.10",
4
+ "version": "0.8.11",
5
5
  "description": "Polkadot app integration",
6
6
  "license": "Apache-2.0",
7
7
  "repository": {
@@ -34,10 +34,10 @@
34
34
  "@noble/ciphers": "2.2.0",
35
35
  "@noble/curves": "2.2.0",
36
36
  "@noble/hashes": "2.2.0",
37
- "@novasamatech/host-api": "0.8.10",
38
- "@novasamatech/scale": "0.8.10",
39
- "@novasamatech/statement-store": "0.8.10",
40
- "@novasamatech/storage-adapter": "0.8.10",
37
+ "@novasamatech/host-api": "0.8.11",
38
+ "@novasamatech/scale": "0.8.11",
39
+ "@novasamatech/statement-store": "0.8.11",
40
+ "@novasamatech/storage-adapter": "0.8.11",
41
41
  "@polkadot-api/utils": "^0.4.0",
42
42
  "@polkadot-labs/hdkd-helpers": "^0.0.30",
43
43
  "nanoevents": "9.1.0",