@novasamatech/statement-store 0.7.6 → 0.7.7

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.
@@ -19,6 +19,7 @@ export function createSession({ localAccount, remoteAccount, statementStore, enc
19
19
  const incomingSessionId = createSessionId(remoteAccount.publicKey, remoteAccount, localAccount);
20
20
  const state = {
21
21
  phase: 'initialization',
22
+ initError: null,
22
23
  expiry: 0n,
23
24
  outgoingRequest: null,
24
25
  incomingRequest: null,
@@ -99,7 +100,7 @@ export function createSession({ localAccount, remoteAccount, statementStore, enc
99
100
  deliverStatementData(statementData);
100
101
  }
101
102
  else if (statementData.tag === 'response') {
102
- if (state.outgoingRequest?.requestId !== statementData.value.requestId)
103
+ if (!state.outgoingRequest?.requestIds.includes(statementData.value.requestId))
103
104
  return;
104
105
  const responseMessage = {
105
106
  type: 'response',
@@ -123,7 +124,7 @@ export function createSession({ localAccount, remoteAccount, statementStore, enc
123
124
  function processNewMessage(encoded, token) {
124
125
  if (state.outgoingRequest === null) {
125
126
  const requestId = nanoid();
126
- state.outgoingRequest = { requestId, messages: [encoded], tokens: [token] };
127
+ state.outgoingRequest = { requestIds: [requestId], messages: [encoded], tokens: [token] };
127
128
  encodeAndSubmitRequest(requestId, state.outgoingRequest.messages);
128
129
  }
129
130
  else {
@@ -131,8 +132,9 @@ export function createSession({ localAccount, remoteAccount, statementStore, enc
131
132
  if (currentTotal + encoded.length <= maxRequestSize) {
132
133
  state.outgoingRequest.messages.push(encoded);
133
134
  state.outgoingRequest.tokens.push(token);
134
- state.outgoingRequest.requestId = nanoid();
135
- encodeAndSubmitRequest(state.outgoingRequest.requestId, state.outgoingRequest.messages);
135
+ const newRequestId = nanoid();
136
+ state.outgoingRequest.requestIds.push(newRequestId);
137
+ encodeAndSubmitRequest(newRequestId, state.outgoingRequest.messages);
136
138
  }
137
139
  else {
138
140
  state.messageQueue.push({ encoded, token });
@@ -140,9 +142,10 @@ export function createSession({ localAccount, remoteAccount, statementStore, enc
140
142
  }
141
143
  }
142
144
  function processMessageQueue() {
143
- const currentTotal = state.outgoingRequest?.messages.reduce((s, m) => s + m.length, 0) ?? 0;
144
145
  while (state.messageQueue.length > 0) {
145
146
  const head = state.messageQueue[0];
147
+ // Recompute per iteration; `processNewMessage` mutates outgoingRequest.messages in place.
148
+ const currentTotal = state.outgoingRequest?.messages.reduce((s, m) => s + m.length, 0) ?? 0;
146
149
  if (state.outgoingRequest !== null && currentTotal + head.encoded.length > maxRequestSize)
147
150
  break;
148
151
  state.messageQueue.shift();
@@ -166,13 +169,31 @@ export function createSession({ localAccount, remoteAccount, statementStore, enc
166
169
  }
167
170
  });
168
171
  }
172
+ function rejectAllPending(error) {
173
+ for (const [, deferred] of state.pendingDelivery) {
174
+ deferred.reject(error);
175
+ }
176
+ state.pendingDelivery.clear();
177
+ }
178
+ function failInit(error) {
179
+ state.phase = 'failed';
180
+ state.initError = error;
181
+ state.messageQueue = [];
182
+ rejectAllPending(error);
183
+ }
169
184
  async function init() {
170
185
  const [ownResult, peerResult] = await Promise.all([
171
186
  statementStore.queryStatements({ matchAll: [outgoingSessionId] }),
172
187
  statementStore.queryStatements({ matchAll: [incomingSessionId] }),
173
188
  ]);
174
- if (ownResult.isErr() || peerResult.isErr())
189
+ if (ownResult.isErr()) {
190
+ failInit(ownResult.error);
191
+ return;
192
+ }
193
+ if (peerResult.isErr()) {
194
+ failInit(peerResult.error);
175
195
  return;
196
+ }
176
197
  const ownStatements = ownResult.value;
177
198
  const peerStatements = peerResult.value;
178
199
  let maxExpiry = 0n;
@@ -195,7 +216,7 @@ export function createSession({ localAccount, remoteAccount, statementStore, enc
195
216
  const hasResponse = ownResponse?.tag === 'response' && ownResponse.value.requestId === ownRequest.value.requestId;
196
217
  if (!hasResponse) {
197
218
  state.outgoingRequest = {
198
- requestId: ownRequest.value.requestId,
219
+ requestIds: [ownRequest.value.requestId],
199
220
  messages: ownRequest.value.data,
200
221
  tokens: [], // tokens from previous session cannot be restored
201
222
  };
@@ -229,6 +250,9 @@ export function createSession({ localAccount, remoteAccount, statementStore, enc
229
250
  const encoded = encodedResult.value;
230
251
  if (encoded.length > maxRequestSize)
231
252
  return errAsync(new Error('message too big'));
253
+ if (state.phase === 'failed') {
254
+ return errAsync(state.initError ?? new Error('Session initialization failed'));
255
+ }
232
256
  const token = nanoid();
233
257
  let resolveFn;
234
258
  let rejectFn;
@@ -314,10 +338,7 @@ export function createSession({ localAccount, remoteAccount, statementStore, enc
314
338
  responseStoreUnsub?.();
315
339
  responseStoreUnsub = null;
316
340
  subscribers = [];
317
- for (const [, deferred] of state.pendingDelivery) {
318
- deferred.reject(new Error('Session disposed'));
319
- }
320
- state.pendingDelivery.clear();
341
+ rejectAllPending(new Error('Session disposed'));
321
342
  },
322
343
  };
323
344
  void init();
@@ -5,7 +5,21 @@ import { describe, expect, it, vi } from 'vitest';
5
5
  import { createAccountId, createLocalSessionAccount, createRemoteSessionAccount } from '../model/sessionAccount.js';
6
6
  import { StatementData } from './scale/statementData.js';
7
7
  import { createSession, nextExpiry } from './session.js';
8
- import { createSr25519Prover } from './statementProver.js';
8
+ // Real signature work belongs in statementProver tests; this stub stamps a
9
+ // non-empty proof so submitted statements are well-formed.
10
+ const mockProver = {
11
+ generateMessageProof: statement => okAsync({
12
+ ...statement,
13
+ proof: {
14
+ type: 'sr25519',
15
+ value: {
16
+ signature: `0x${'00'.repeat(64)}`,
17
+ signer: `0x${'00'.repeat(32)}`,
18
+ },
19
+ },
20
+ }),
21
+ verifyMessageProof: () => okAsync(true),
22
+ };
9
23
  function makeAccounts() {
10
24
  const localAccount = createLocalSessionAccount(createAccountId(new Uint8Array(32).fill(1)));
11
25
  const remoteAccount = createRemoteSessionAccount(createAccountId(new Uint8Array(32).fill(2)), new Uint8Array(32).fill(3));
@@ -42,7 +56,7 @@ function makeSession(overrides) {
42
56
  remoteAccount,
43
57
  statementStore: adapter,
44
58
  encryption: mockEncryption(),
45
- prover: createSr25519Prover(new Uint8Array(64).fill(1)),
59
+ prover: mockProver,
46
60
  maxRequestSize,
47
61
  });
48
62
  return { session, adapter };
@@ -14,8 +14,7 @@ export function createSr25519Prover(secret) {
14
14
  verifyMessageProof(statement) {
15
15
  const { proof, ...unsigned } = statement;
16
16
  if (!proof) {
17
- // TODO should we pass check when proof is not presented?
18
- return okAsync(true);
17
+ return errAsync(new Error('Proof is not provided'));
19
18
  }
20
19
  const encoded = statementCodec.enc(unsigned);
21
20
  const compactLen = compact.enc(compact.dec(encoded)).length;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@novasamatech/statement-store",
3
3
  "type": "module",
4
- "version": "0.7.6",
4
+ "version": "0.7.7",
5
5
  "description": "Statement store integration",
6
6
  "license": "Apache-2.0",
7
7
  "repository": {
@@ -25,7 +25,7 @@
25
25
  "README.md"
26
26
  ],
27
27
  "dependencies": {
28
- "@novasamatech/scale": "0.7.6",
28
+ "@novasamatech/scale": "0.7.7",
29
29
  "@novasamatech/sdk-statement": "^0.6.0",
30
30
  "@polkadot-api/substrate-bindings": "^0.20.1",
31
31
  "@polkadot-api/substrate-client": "^0.7.0",