@novasamatech/statement-store 0.6.4 → 0.6.5

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.
@@ -1,7 +1,10 @@
1
+ import type { RequestFn, SubscribeFn } from '@novasamatech/sdk-statement';
1
2
  import type { JsonRpcProvider } from '@polkadot-api/json-rpc-provider';
2
3
  import type { PolkadotClient } from 'polkadot-api';
3
4
  export type LazyClient = ReturnType<typeof createLazyClient>;
4
5
  export declare const createLazyClient: (provider: JsonRpcProvider) => {
5
6
  getClient(): PolkadotClient;
7
+ getRequestFn(): RequestFn;
8
+ getSubscribeFn(): SubscribeFn;
6
9
  disconnect(): void;
7
10
  };
@@ -1,16 +1,47 @@
1
- import { createClient } from 'polkadot-api';
1
+ import { createClient as createSubstrateClient } from '@polkadot-api/substrate-client';
2
+ import { createClient as createPolkadotClient } from 'polkadot-api';
2
3
  export const createLazyClient = (provider) => {
3
- let client = null;
4
+ let polkadotClient = null;
5
+ let substrateClient = null;
6
+ function getSubstrateClient() {
7
+ if (!substrateClient) {
8
+ substrateClient = createSubstrateClient(provider);
9
+ }
10
+ return substrateClient;
11
+ }
4
12
  return {
5
13
  getClient() {
6
- if (!client) {
7
- client = createClient(provider);
14
+ if (!polkadotClient) {
15
+ polkadotClient = createPolkadotClient(provider);
8
16
  }
9
- return client;
17
+ return polkadotClient;
18
+ },
19
+ getRequestFn() {
20
+ const c = getSubstrateClient();
21
+ return (method, params) => new Promise((resolve, reject) => {
22
+ c._request(method, params, {
23
+ onSuccess: result => resolve(result),
24
+ onError: e => reject(e),
25
+ });
26
+ });
27
+ },
28
+ getSubscribeFn() {
29
+ const c = getSubstrateClient();
30
+ return (method, params, onMessage, onError) => {
31
+ return c._request(method, params, {
32
+ onSuccess: (subscriptionId, followSubscription) => {
33
+ followSubscription(subscriptionId, { next: onMessage, error: onError });
34
+ },
35
+ onError,
36
+ });
37
+ };
10
38
  },
11
39
  disconnect() {
12
- if (client) {
13
- client.destroy();
40
+ if (polkadotClient) {
41
+ polkadotClient.destroy();
42
+ }
43
+ if (substrateClient) {
44
+ substrateClient.destroy();
14
45
  }
15
46
  },
16
47
  };
@@ -1,66 +1,84 @@
1
- import { createStatementSdk } from '@polkadot-api/sdk-statement';
2
- import { Binary } from '@polkadot-api/substrate-bindings';
1
+ import { createStatementSdk } from '@novasamatech/sdk-statement';
3
2
  import { toHex } from '@polkadot-api/utils';
4
3
  import { errAsync, fromPromise, okAsync } from 'neverthrow';
5
4
  import { toError } from '../helpers.js';
6
- import { AccountFullError, BadProofError, DataTooLargeError, EncodingTooLargeError, NoProofError, PriorityTooLowError, StorageFullError, } from './types.js';
7
- const POLLING_INTERVAL = 1500;
5
+ import { AccountFullError, AlreadyExpiredError, BadProofError, DataTooLargeError, EncodingTooLargeError, ExpiryTooLowError, InternalStoreError, KnownExpiredError, NoAllowanceError, NoProofError, StorageFullError, } from './types.js';
8
6
  function createKey(topics) {
9
7
  return topics.map(toHex).sort().join('');
10
8
  }
9
+ function toTopicFilter(topics) {
10
+ if (topics.length === 0)
11
+ return 'any';
12
+ return { matchAll: topics.map(t => toHex(t)) };
13
+ }
11
14
  export function createPapiStatementStoreAdapter(lazyClient) {
12
- const sdk = createStatementSdk((method, params) => {
13
- const client = lazyClient.getClient();
14
- return client._request(method, params);
15
- });
16
- const pollings = new Map();
17
- const subscriptions = new Map();
18
- function addSubscription(key, subscription) {
19
- let subs = subscriptions.get(key);
20
- if (!subs) {
21
- subs = [];
22
- subscriptions.set(key, subs);
15
+ const sdk = createStatementSdk(lazyClient.getRequestFn(), lazyClient.getSubscribeFn());
16
+ const activeSubscriptions = new Map();
17
+ const callbacks = new Map();
18
+ function addCallback(key, callback) {
19
+ let list = callbacks.get(key);
20
+ if (!list) {
21
+ list = [];
22
+ callbacks.set(key, list);
23
23
  }
24
- subs.push(subscription);
25
- return subs;
24
+ list.push(callback);
25
+ return list;
26
26
  }
27
- function removeSubscription(key, subscription) {
28
- let subs = subscriptions.get(key);
29
- if (!subs) {
27
+ function removeCallback(key, callback) {
28
+ let list = callbacks.get(key);
29
+ if (!list)
30
30
  return [];
31
+ list = list.filter(x => x !== callback);
32
+ if (list.length === 0) {
33
+ callbacks.delete(key);
34
+ }
35
+ else {
36
+ callbacks.set(key, list);
31
37
  }
32
- subs = subs.filter(x => x !== subscription);
33
- return subs;
38
+ return list;
34
39
  }
35
- const transportProvider = {
36
- queryStatements(topics, destination) {
37
- return fromPromise(sdk.getStatements({
38
- // @ts-expect-error unmatched types of @polkadot-api/sdk-statement and @polkadot-api/substrate-bindings
39
- topics: topics.map(t => Binary.fromBytes(t)),
40
- // @ts-expect-error unmatched types of @polkadot-api/sdk-statement and @polkadot-api/substrate-bindings
41
- dest: destination ? Binary.fromBytes(destination) : null,
42
- }), toError);
40
+ const adapter = {
41
+ queryStatements(topics) {
42
+ const filter = toTopicFilter(topics);
43
+ return fromPromise(sdk.getStatements(filter), toError);
43
44
  },
44
45
  subscribeStatements(topics, callback) {
45
46
  const key = createKey(topics);
46
- const callbacks = addSubscription(key, callback);
47
- if (callbacks.length === 1) {
48
- const unsub = polling(POLLING_INTERVAL, () => transportProvider.queryStatements(topics), statements => {
49
- const list = subscriptions.get(key);
50
- if (list) {
51
- for (const fn of list) {
47
+ const list = addCallback(key, callback);
48
+ if (list.length === 1) {
49
+ const filter = toTopicFilter(topics);
50
+ let batch = [];
51
+ let flushScheduled = false;
52
+ const flush = () => {
53
+ flushScheduled = false;
54
+ if (batch.length === 0)
55
+ return;
56
+ const statements = batch;
57
+ batch = [];
58
+ const currentCallbacks = callbacks.get(key);
59
+ if (currentCallbacks) {
60
+ for (const fn of currentCallbacks) {
52
61
  fn(statements);
53
62
  }
54
63
  }
64
+ };
65
+ const unsub = sdk.subscribeStatements(filter, statement => {
66
+ batch.push(statement);
67
+ if (!flushScheduled) {
68
+ flushScheduled = true;
69
+ setTimeout(flush, 0);
70
+ }
71
+ }, error => {
72
+ console.error('Statement subscription error:', error);
55
73
  });
56
- pollings.set(key, unsub);
74
+ activeSubscriptions.set(key, unsub);
57
75
  }
58
76
  return () => {
59
- const callbacks = removeSubscription(key, callback);
60
- if (callbacks.length === 0) {
61
- const stopPolling = pollings.get(key);
62
- stopPolling?.();
63
- pollings.delete(key);
77
+ const remaining = removeCallback(key, callback);
78
+ if (remaining.length === 0) {
79
+ const unsub = activeSubscriptions.get(key);
80
+ unsub?.();
81
+ activeSubscriptions.delete(key);
64
82
  }
65
83
  };
66
84
  },
@@ -70,16 +88,22 @@ export function createPapiStatementStoreAdapter(lazyClient) {
70
88
  case 'new':
71
89
  case 'known':
72
90
  return okAsync(undefined);
91
+ case 'knownExpired':
92
+ return errAsync(new KnownExpiredError());
93
+ case 'internalError':
94
+ return errAsync(new InternalStoreError(result.error));
73
95
  case 'rejected':
74
96
  switch (result.reason) {
75
97
  case 'dataTooLarge':
76
98
  return errAsync(new DataTooLargeError(result.submitted_size, result.available_size));
77
99
  case 'channelPriorityTooLow':
78
- return errAsync(new PriorityTooLowError(result.submitted_priority, result.min_priority));
100
+ return errAsync(new ExpiryTooLowError(result.submitted_expiry, result.min_expiry));
79
101
  case 'accountFull':
80
- return errAsync(new AccountFullError());
102
+ return errAsync(new AccountFullError(result.submitted_expiry, result.min_expiry));
81
103
  case 'storeFull':
82
104
  return errAsync(new StorageFullError());
105
+ case 'noAllowance':
106
+ return errAsync(new NoAllowanceError());
83
107
  default:
84
108
  return errAsync(new Error('Unknown rejection reason'));
85
109
  }
@@ -91,6 +115,8 @@ export function createPapiStatementStoreAdapter(lazyClient) {
91
115
  return errAsync(new BadProofError());
92
116
  case 'encodingTooLarge':
93
117
  return errAsync(new EncodingTooLargeError(result.submitted_size, result.max_size));
118
+ case 'alreadyExpired':
119
+ return errAsync(new AlreadyExpiredError());
94
120
  default:
95
121
  return errAsync(new Error('Unknown rejection reason: invalid'));
96
122
  }
@@ -100,29 +126,5 @@ export function createPapiStatementStoreAdapter(lazyClient) {
100
126
  });
101
127
  },
102
128
  };
103
- return transportProvider;
104
- }
105
- function polling(interval, request, callback) {
106
- let active = true;
107
- let tm = null;
108
- function createCycle() {
109
- tm = setTimeout(() => {
110
- if (!active) {
111
- return;
112
- }
113
- request().match(data => {
114
- callback(data);
115
- createCycle();
116
- }, () => {
117
- createCycle();
118
- });
119
- }, interval);
120
- }
121
- createCycle();
122
- return () => {
123
- active = false;
124
- if (tm !== null) {
125
- clearTimeout(tm);
126
- }
127
- };
129
+ return adapter;
128
130
  }
@@ -1,22 +1,24 @@
1
- import type { SignedStatement, Statement } from '@polkadot-api/sdk-statement';
1
+ import type { SignedStatement, Statement } from '@novasamatech/sdk-statement';
2
2
  import type { ResultAsync } from 'neverthrow';
3
3
  export type StatementStoreAdapter = {
4
4
  queryStatements(topics: Uint8Array[], destination?: Uint8Array): ResultAsync<Statement[], Error>;
5
5
  subscribeStatements(topics: Uint8Array[], callback: (statements: Statement[]) => unknown): VoidFunction;
6
- submitStatement(statement: SignedStatement): ResultAsync<void, DataTooLargeError | PriorityTooLowError | AccountFullError | StorageFullError | NoProofError | BadProofError | EncodingTooLargeError | Error>;
6
+ submitStatement(statement: SignedStatement): ResultAsync<void, DataTooLargeError | ExpiryTooLowError | AccountFullError | StorageFullError | NoProofError | BadProofError | EncodingTooLargeError | NoAllowanceError | AlreadyExpiredError | KnownExpiredError | InternalStoreError | Error>;
7
7
  };
8
8
  export declare class DataTooLargeError extends Error {
9
9
  readonly submitted: number;
10
10
  readonly available: number;
11
11
  constructor(submitted: number, available: number);
12
12
  }
13
- export declare class PriorityTooLowError extends Error {
14
- readonly submitted: number;
15
- readonly min: number;
16
- constructor(submitted: number, min: number);
13
+ export declare class ExpiryTooLowError extends Error {
14
+ readonly submitted: bigint;
15
+ readonly min: bigint;
16
+ constructor(submitted: bigint, min: bigint);
17
17
  }
18
18
  export declare class AccountFullError extends Error {
19
- constructor();
19
+ readonly submitted: bigint;
20
+ readonly min: bigint;
21
+ constructor(submitted: bigint, min: bigint);
20
22
  }
21
23
  export declare class StorageFullError extends Error {
22
24
  constructor();
@@ -32,3 +34,15 @@ export declare class EncodingTooLargeError extends Error {
32
34
  readonly max: number;
33
35
  constructor(submitted: number, max: number);
34
36
  }
37
+ export declare class NoAllowanceError extends Error {
38
+ constructor();
39
+ }
40
+ export declare class AlreadyExpiredError extends Error {
41
+ constructor();
42
+ }
43
+ export declare class KnownExpiredError extends Error {
44
+ constructor();
45
+ }
46
+ export declare class InternalStoreError extends Error {
47
+ constructor(detail: string);
48
+ }
@@ -7,18 +7,22 @@ export class DataTooLargeError extends Error {
7
7
  this.available = available;
8
8
  }
9
9
  }
10
- export class PriorityTooLowError extends Error {
10
+ export class ExpiryTooLowError extends Error {
11
11
  submitted;
12
12
  min;
13
13
  constructor(submitted, min) {
14
- super(`Submit failed, priority too low: ${submitted} > ${min}`);
14
+ super(`Submit failed, expiry too low: ${submitted} < ${min}`);
15
15
  this.submitted = submitted;
16
16
  this.min = min;
17
17
  }
18
18
  }
19
19
  export class AccountFullError extends Error {
20
- constructor() {
21
- super(`Submit failed, account full`);
20
+ submitted;
21
+ min;
22
+ constructor(submitted, min) {
23
+ super(`Submit failed, account full: submitted expiry ${submitted} < min ${min}`);
24
+ this.submitted = submitted;
25
+ this.min = min;
22
26
  }
23
27
  }
24
28
  export class StorageFullError extends Error {
@@ -45,3 +49,23 @@ export class EncodingTooLargeError extends Error {
45
49
  this.max = max;
46
50
  }
47
51
  }
52
+ export class NoAllowanceError extends Error {
53
+ constructor() {
54
+ super(`Submit failed, no allowance set for account`);
55
+ }
56
+ }
57
+ export class AlreadyExpiredError extends Error {
58
+ constructor() {
59
+ super(`Submit failed, statement already expired`);
60
+ }
61
+ }
62
+ export class KnownExpiredError extends Error {
63
+ constructor() {
64
+ super(`Submit failed, statement was known but has expired`);
65
+ }
66
+ }
67
+ export class InternalStoreError extends Error {
68
+ constructor(detail) {
69
+ super(`Submit failed, internal store error: ${detail}`);
70
+ }
71
+ }
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export type { Statement } from '@polkadot-api/sdk-statement';
1
+ export type { Statement } from '@novasamatech/sdk-statement';
2
2
  export type { SessionId } from './model/session.js';
3
3
  export { SessionIdCodec, createSessionId } from './model/session.js';
4
4
  export type { AccountId, LocalSessionAccount, RemoteSessionAccount, SessionAccount } from './model/sessionAccount.js';
@@ -14,5 +14,6 @@ export { DecodingError, DecryptionError, UnknownError } from './session/error.js
14
14
  export type { LazyClient } from './adapter/lazyClient.js';
15
15
  export { createLazyClient } from './adapter/lazyClient.js';
16
16
  export type { StatementStoreAdapter } from './adapter/types.js';
17
+ export { AccountFullError, AlreadyExpiredError, BadProofError, DataTooLargeError, EncodingTooLargeError, ExpiryTooLowError, InternalStoreError, KnownExpiredError, NoAllowanceError, NoProofError, StorageFullError, } from './adapter/types.js';
17
18
  export { createPapiStatementStoreAdapter } from './adapter/rpc.js';
18
19
  export { createSr25519Derivation, createSr25519Secret, deriveSr25519PublicKey, khash, signWithSr25519Secret, verifySr25519Signature, } from './crypto.js';
package/dist/index.js CHANGED
@@ -6,5 +6,6 @@ export { createSr25519Prover } from './session/statementProver.js';
6
6
  export { createEncryption } from './session/encyption.js';
7
7
  export { DecodingError, DecryptionError, UnknownError } from './session/error.js';
8
8
  export { createLazyClient } from './adapter/lazyClient.js';
9
+ export { AccountFullError, AlreadyExpiredError, BadProofError, DataTooLargeError, EncodingTooLargeError, ExpiryTooLowError, InternalStoreError, KnownExpiredError, NoAllowanceError, NoProofError, StorageFullError, } from './adapter/types.js';
9
10
  export { createPapiStatementStoreAdapter } from './adapter/rpc.js';
10
11
  export { createSr25519Derivation, createSr25519Secret, deriveSr25519PublicKey, khash, signWithSr25519Secret, verifySr25519Signature, } from './crypto.js';
@@ -1,4 +1,5 @@
1
- import { Binary } from '@polkadot-api/substrate-bindings';
1
+ import { createExpiryFromDuration } from '@novasamatech/sdk-statement';
2
+ import { toHex } from '@polkadot-api/utils';
2
3
  import { nanoid } from 'nanoid';
3
4
  import { ResultAsync, err, fromPromise, fromThrowable, ok, okAsync } from 'neverthrow';
4
5
  import { Bytes } from 'scale-ts';
@@ -14,13 +15,10 @@ export function createSession({ localAccount, remoteAccount, statementStore, enc
14
15
  return encryption
15
16
  .encrypt(data)
16
17
  .map(data => ({
17
- priority: getPriority(now()),
18
- // @ts-expect-error unmatched types of @polkadot-api/sdk-statement and @polkadot-api/substrate-bindings
19
- channel: Binary.fromBytes(channel),
20
- // @ts-expect-error unmatched types of @polkadot-api/sdk-statement and @polkadot-api/substrate-bindings
21
- topics: [Binary.fromBytes(sessionId)],
22
- // @ts-expect-error unmatched types of @polkadot-api/sdk-statement and @polkadot-api/substrate-bindings
23
- data: Binary.fromBytes(data),
18
+ expiry: getExpiry(),
19
+ channel: toHex(channel),
20
+ topics: [toHex(sessionId)],
21
+ data,
24
22
  }))
25
23
  .asyncAndThen(prover.generateMessageProof)
26
24
  .andThen(statementStore.submitStatement);
@@ -90,7 +88,7 @@ export function createSession({ localAccount, remoteAccount, statementStore, enc
90
88
  function processStatement(statement) {
91
89
  if (!statement.data)
92
90
  return okAsync(null);
93
- const data = statement.data.asBytes();
91
+ const data = statement.data;
94
92
  return prover
95
93
  .verifyMessageProof(statement)
96
94
  .andThen(verified => (verified ? ok() : err(new Error('Statement proof is not valid'))))
@@ -135,14 +133,9 @@ function mapResponseCode(responseCode) {
135
133
  return err(new UnknownError());
136
134
  }
137
135
  }
138
- function now() {
139
- const d1 = new Date();
140
- const d2 = new Date(d1.getUTCFullYear(), d1.getUTCMonth(), d1.getUTCDate(), d1.getUTCHours(), d1.getUTCMinutes(), d1.getUTCSeconds());
141
- return d2.getTime();
142
- }
143
- function getPriority(timestamp) {
144
- // time - (November 15, 2025)
145
- return Math.floor((timestamp - 1763154000000) / 1000);
136
+ const DEFAULT_EXPIRY_DURATION_SECS = 7 * 24 * 60 * 60; // 7 days
137
+ function getExpiry() {
138
+ return createExpiryFromDuration(DEFAULT_EXPIRY_DURATION_SECS);
146
139
  }
147
140
  function createRequestChannel(sessionId) {
148
141
  return khash(sessionId, stringToBytes('request'));
@@ -1,4 +1,4 @@
1
- import type { SignedStatement, Statement } from '@polkadot-api/sdk-statement';
1
+ import type { SignedStatement, Statement } from '@novasamatech/sdk-statement';
2
2
  import type { ResultAsync } from 'neverthrow';
3
3
  export type StatementProver = {
4
4
  generateMessageProof(statement: Statement): ResultAsync<SignedStatement, Error>;
@@ -1,6 +1,7 @@
1
- import { getStatementSigner, statementCodec } from '@polkadot-api/sdk-statement';
1
+ import { getStatementSigner, statementCodec } from '@novasamatech/sdk-statement';
2
+ import { compact } from '@polkadot-api/substrate-bindings';
3
+ import { fromHex } from '@polkadot-api/utils';
2
4
  import { errAsync, fromPromise, fromThrowable, okAsync } from 'neverthrow';
3
- import { compact } from 'scale-ts';
4
5
  import { deriveSr25519PublicKey, signWithSr25519Secret, verifySr25519Signature } from '../crypto.js';
5
6
  import { toError } from '../helpers.js';
6
7
  export function createSr25519Prover(secret) {
@@ -20,7 +21,7 @@ export function createSr25519Prover(secret) {
20
21
  const compactLen = compact.enc(compact.dec(encoded)).length;
21
22
  switch (proof.type) {
22
23
  case 'sr25519':
23
- return verify(encoded.slice(compactLen), proof.value.signature.asBytes(), proof.value.signer.asBytes()).asyncAndThen(x => okAsync(x));
24
+ return verify(encoded.slice(compactLen), fromHex(proof.value.signature), fromHex(proof.value.signer)).asyncAndThen(x => okAsync(x));
24
25
  default:
25
26
  return errAsync(new Error(`Proof type ${proof.type} is not supported.`));
26
27
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@novasamatech/statement-store",
3
3
  "type": "module",
4
- "version": "0.6.4",
4
+ "version": "0.6.5",
5
5
  "description": "Statement store integration",
6
6
  "license": "Apache-2.0",
7
7
  "repository": {
@@ -24,8 +24,9 @@
24
24
  "README.md"
25
25
  ],
26
26
  "dependencies": {
27
- "@polkadot-api/sdk-statement": "^0.3.0",
27
+ "@novasamatech/sdk-statement": "^0.5.0",
28
28
  "@polkadot-api/substrate-bindings": "^0.17.0",
29
+ "@polkadot-api/substrate-client": "^0.5.0",
29
30
  "@polkadot-api/utils": "0.2.0",
30
31
  "@polkadot-labs/hdkd-helpers": "0.0.27",
31
32
  "@noble/hashes": "2.0.1",