@novasamatech/statement-store 0.5.0-17 → 0.5.0-18
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/model/channel.d.ts +6 -0
- package/dist/model/channel.js +9 -0
- package/dist/session/mappers.d.ts +10 -0
- package/dist/session/mappers.js +41 -0
- package/dist/session/priority.d.ts +2 -0
- package/dist/session/priority.js +13 -0
- package/dist/session/requestId.d.ts +1 -0
- package/dist/session/requestId.js +2 -0
- package/dist/session/session.js +0 -2
- package/dist/session/tracker.d.ts +6 -0
- package/dist/session/tracker.js +26 -0
- package/dist/session/transport.d.ts +20 -0
- package/dist/session/transport.js +76 -0
- package/package.json +1 -1
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Branded } from '../types.js';
|
|
2
|
+
import type { SessionId } from './session.js';
|
|
3
|
+
export type Channel = Branded<Uint8Array, 'Channel'>;
|
|
4
|
+
export declare const ChannelCodec: import("scale-ts").Codec<Uint8Array<ArrayBufferLike>>;
|
|
5
|
+
export declare function createRequestChannel(sessionId: SessionId): Channel;
|
|
6
|
+
export declare function createResponseChannel(sessionId: SessionId): Channel;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Bytes } from 'scale-ts';
|
|
2
|
+
import { khash, stringToBytes } from '../crypto.js';
|
|
3
|
+
export const ChannelCodec = Bytes(32);
|
|
4
|
+
export function createRequestChannel(sessionId) {
|
|
5
|
+
return khash(sessionId, stringToBytes('request'));
|
|
6
|
+
}
|
|
7
|
+
export function createResponseChannel(sessionId) {
|
|
8
|
+
return khash(sessionId, stringToBytes('response'));
|
|
9
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Statement } from '@polkadot-api/sdk-statement';
|
|
2
|
+
import type { CodecType } from 'scale-ts';
|
|
3
|
+
import type { Channel } from '../model/channel.js';
|
|
4
|
+
import type { SessionId } from '../model/session.js';
|
|
5
|
+
import { DecodingError } from './error.js';
|
|
6
|
+
import type { ResponseCode, StatementData } from './scale/statementData.js';
|
|
7
|
+
import type { SessionEvent } from './types.js';
|
|
8
|
+
export declare function toStatement(sessionId: SessionId, channel: Channel, priority: number, data: Uint8Array): Statement;
|
|
9
|
+
export declare function toEvent(statement: Statement, data: CodecType<typeof StatementData>): SessionEvent;
|
|
10
|
+
export declare function mapResponseCode(responseCode: ResponseCode): import("neverthrow").Ok<void, never> | import("neverthrow").Err<never, DecodingError>;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { Binary } from '@polkadot-api/substrate-bindings';
|
|
2
|
+
import { err, ok } from 'neverthrow';
|
|
3
|
+
import { DecodingError, DecryptionError, UnknownError } from './error.js';
|
|
4
|
+
export function toStatement(sessionId, channel, priority, data) {
|
|
5
|
+
return {
|
|
6
|
+
priority: priority,
|
|
7
|
+
channel: Binary.fromBytes(channel),
|
|
8
|
+
topics: [Binary.fromBytes(sessionId)],
|
|
9
|
+
data: Binary.fromBytes(data),
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
export function toEvent(statement, data) {
|
|
13
|
+
switch (data.tag) {
|
|
14
|
+
case 'request':
|
|
15
|
+
return {
|
|
16
|
+
type: 'request',
|
|
17
|
+
requestId: data.value.requestId,
|
|
18
|
+
priority: statement.priority ?? 0,
|
|
19
|
+
messages: data.value.data,
|
|
20
|
+
};
|
|
21
|
+
case 'response':
|
|
22
|
+
return {
|
|
23
|
+
type: 'response',
|
|
24
|
+
requestId: data.value.requestId,
|
|
25
|
+
priority: statement.priority ?? 0,
|
|
26
|
+
responseCode: data.value.responseCode,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
export function mapResponseCode(responseCode) {
|
|
31
|
+
switch (responseCode) {
|
|
32
|
+
case 'success':
|
|
33
|
+
return ok();
|
|
34
|
+
case 'decodingFailed':
|
|
35
|
+
return err(new DecodingError());
|
|
36
|
+
case 'decryptionFailed':
|
|
37
|
+
return err(new DecryptionError());
|
|
38
|
+
case 'unknown':
|
|
39
|
+
return err(new UnknownError());
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
function now() {
|
|
2
|
+
const d1 = new Date();
|
|
3
|
+
const d2 = new Date(d1.getUTCFullYear(), d1.getUTCMonth(), d1.getUTCDate(), d1.getUTCHours(), d1.getUTCMinutes(), d1.getUTCSeconds());
|
|
4
|
+
return d2.getTime();
|
|
5
|
+
}
|
|
6
|
+
export function getDefaultPriority() {
|
|
7
|
+
return getPriority(now());
|
|
8
|
+
}
|
|
9
|
+
export function getPriority(timestamp) {
|
|
10
|
+
// (November 15, 2025) in seconds
|
|
11
|
+
const BASE_PRIORITY = 1763154000;
|
|
12
|
+
return Math.floor(timestamp / 1000) - BASE_PRIORITY;
|
|
13
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const createRequestId: () => string;
|
package/dist/session/session.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { Binary } from '@polkadot-api/substrate-bindings';
|
|
2
|
-
import { toHex } from '@polkadot-api/utils';
|
|
3
2
|
import { nanoid } from 'nanoid';
|
|
4
3
|
import { ResultAsync, err, fromPromise, fromThrowable, ok, okAsync } from 'neverthrow';
|
|
5
4
|
import { Bytes } from 'scale-ts';
|
|
@@ -12,7 +11,6 @@ import { StatementData } from './scale/statementData.js';
|
|
|
12
11
|
export function createSession({ localAccount, remoteAccount, statementStore, encryption, prover, }) {
|
|
13
12
|
let subscriptions = [];
|
|
14
13
|
function submit(sessionId, channel, data) {
|
|
15
|
-
console.log('data', toHex(data));
|
|
16
14
|
return encryption
|
|
17
15
|
.encrypt(data)
|
|
18
16
|
.map(data => ({
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { toHex } from '@polkadot-api/utils';
|
|
2
|
+
export const createTracker = () => {
|
|
3
|
+
const tracked = new Set();
|
|
4
|
+
function toHash(payload) {
|
|
5
|
+
switch (payload.type) {
|
|
6
|
+
case 'request': {
|
|
7
|
+
let hash = `${payload.requestId}_${payload.priority}_`;
|
|
8
|
+
for (const message of payload.messages) {
|
|
9
|
+
hash += toHex(message);
|
|
10
|
+
}
|
|
11
|
+
return hash;
|
|
12
|
+
}
|
|
13
|
+
case 'response': {
|
|
14
|
+
return `${payload.requestId}_${payload.priority}_${payload.responseCode}`;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return {
|
|
19
|
+
track(payload) {
|
|
20
|
+
tracked.add(toHash(payload));
|
|
21
|
+
},
|
|
22
|
+
isTracked(payload) {
|
|
23
|
+
return tracked.has(toHash(payload));
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { ResultAsync } from 'neverthrow';
|
|
2
|
+
import type { StatementStoreAdapter } from '../adapter/types.js';
|
|
3
|
+
import type { SessionId } from '../model/session.js';
|
|
4
|
+
import type { Callback } from '../types.js';
|
|
5
|
+
import type { Encryption } from './encyption.js';
|
|
6
|
+
import type { StatementProver } from './statementProver.js';
|
|
7
|
+
import type { SessionEvent, SessionRequestEvent, SessionResponseEvent, Subscriber } from './types.js';
|
|
8
|
+
export type Transport = {
|
|
9
|
+
submitRequest(sessionId: SessionId, request: SessionRequestEvent): ResultAsync<void, Error>;
|
|
10
|
+
submitResponse(sessionId: SessionId, response: SessionResponseEvent): ResultAsync<void, Error>;
|
|
11
|
+
query(sessionId: SessionId): ResultAsync<SessionEvent[], Error>;
|
|
12
|
+
subscribe(sessionId: SessionId, callback: Callback<SessionEvent>): Subscriber;
|
|
13
|
+
};
|
|
14
|
+
type Params = {
|
|
15
|
+
statementStore: StatementStoreAdapter;
|
|
16
|
+
encryption: Encryption;
|
|
17
|
+
prover: StatementProver;
|
|
18
|
+
};
|
|
19
|
+
export declare function createTransport({ encryption, prover, statementStore }: Params): Transport;
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { statementCodec } from '@polkadot-api/sdk-statement';
|
|
2
|
+
import { ResultAsync, err, errAsync, fromThrowable, ok } from 'neverthrow';
|
|
3
|
+
import { toError } from '../helpers.js';
|
|
4
|
+
import { createRequestChannel, createResponseChannel } from '../model/channel.js';
|
|
5
|
+
import { DecodingError, DecryptionError, MessageTooLarge } from './error.js';
|
|
6
|
+
import { toEvent, toStatement } from './mappers.js';
|
|
7
|
+
import { StatementData } from './scale/statementData.js';
|
|
8
|
+
import { createTracker } from './tracker.js';
|
|
9
|
+
export function createTransport({ encryption, prover, statementStore }) {
|
|
10
|
+
const tracker = createTracker();
|
|
11
|
+
const encode = fromThrowable(StatementData.enc, toError);
|
|
12
|
+
const decode = fromThrowable(StatementData.dec, () => new DecodingError());
|
|
13
|
+
function validateStatementSize(statement) {
|
|
14
|
+
const MAX_REQUEST_SIZE = 1024;
|
|
15
|
+
const size = statementCodec.enc(statement).length;
|
|
16
|
+
return statementCodec.enc(statement).length > MAX_REQUEST_SIZE
|
|
17
|
+
? err(new MessageTooLarge(size, MAX_REQUEST_SIZE))
|
|
18
|
+
: ok();
|
|
19
|
+
}
|
|
20
|
+
function processStatement(statement) {
|
|
21
|
+
if (!statement.data)
|
|
22
|
+
return errAsync(new Error('Statement.data is not presented'));
|
|
23
|
+
const data = statement.data.asBytes();
|
|
24
|
+
return prover
|
|
25
|
+
.verifyMessageProof(statement)
|
|
26
|
+
.andThen(() => encryption.decrypt(data))
|
|
27
|
+
.andThen(decode)
|
|
28
|
+
.map(statementData => toEvent(statement, statementData));
|
|
29
|
+
}
|
|
30
|
+
function submit(sessionId, channel, priority, data) {
|
|
31
|
+
return encryption
|
|
32
|
+
.encrypt(data)
|
|
33
|
+
.map(data => toStatement(sessionId, channel, priority, data))
|
|
34
|
+
.asyncAndThen(prover.generateMessageProof)
|
|
35
|
+
.andThrough(validateStatementSize)
|
|
36
|
+
.andThen(statementStore.submitStatement);
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
submitRequest(sessionId, request) {
|
|
40
|
+
const rawData = encode({
|
|
41
|
+
tag: 'request',
|
|
42
|
+
value: { requestId: request.requestId, data: request.messages },
|
|
43
|
+
});
|
|
44
|
+
return rawData.asyncAndThen(data => submit(sessionId, createRequestChannel(sessionId), request.priority, data));
|
|
45
|
+
},
|
|
46
|
+
submitResponse(sessionId, response) {
|
|
47
|
+
const rawData = encode({
|
|
48
|
+
tag: 'response',
|
|
49
|
+
value: { requestId: response.requestId, responseCode: response.responseCode },
|
|
50
|
+
});
|
|
51
|
+
return rawData.asyncAndThen(data => submit(sessionId, createResponseChannel(sessionId), response.priority, data));
|
|
52
|
+
},
|
|
53
|
+
query(sessionId) {
|
|
54
|
+
return statementStore
|
|
55
|
+
.queryStatements([sessionId])
|
|
56
|
+
.andThen(statements => ResultAsync.combine(statements.map(processStatement)));
|
|
57
|
+
},
|
|
58
|
+
subscribe(sessionId, callback) {
|
|
59
|
+
const unsubscribe = statementStore.subscribeStatements([sessionId], statements => {
|
|
60
|
+
statements.map(processStatement).forEach(result => {
|
|
61
|
+
result.match(event => {
|
|
62
|
+
if (tracker.isTracked(event))
|
|
63
|
+
return;
|
|
64
|
+
tracker.track(event);
|
|
65
|
+
callback(event);
|
|
66
|
+
}, e => {
|
|
67
|
+
if (DecryptionError.isDecryptionError(e) || DecodingError.isDecodingError(e)) {
|
|
68
|
+
console.error(e);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
return { unsubscribe };
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
}
|