@novasamatech/host-papp 0.5.0-5 → 0.5.0-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.
- package/dist/adapters/identity/rpc.d.ts +4 -2
- package/dist/adapters/identity/rpc.js +96 -26
- package/dist/adapters/identity/types.d.ts +3 -1
- package/dist/adapters/lazyClient/papi.js +5 -0
- package/dist/adapters/lazyClient/types.d.ts +1 -0
- package/dist/adapters/statement/rpc.js +58 -10
- package/dist/adapters/statement/types.d.ts +6 -3
- package/dist/adapters/storage/localStorage.js +26 -4
- package/dist/adapters/storage/memory.js +14 -4
- package/dist/adapters/storage/types.d.ts +5 -2
- package/dist/adapters/storage/types.js +1 -1
- package/dist/components/auth/codec.d.ts +9 -0
- package/dist/components/auth/codec.js +10 -0
- package/dist/components/auth/codecs.d.ts +9 -0
- package/dist/components/auth/codecs.js +10 -0
- package/dist/components/auth/index.d.ts +36 -0
- package/dist/components/auth/index.js +150 -0
- package/dist/components/auth/types.d.ts +15 -0
- package/dist/components/auth/types.js +1 -0
- package/dist/components/session.d.ts +34 -0
- package/dist/components/session.js +54 -0
- package/dist/components/transport.d.ts +27 -0
- package/dist/components/transport.js +57 -0
- package/dist/components/user/codec.d.ts +16 -0
- package/dist/components/user/codec.js +13 -0
- package/dist/components/user/index.d.ts +22 -0
- package/dist/components/user/index.js +58 -0
- package/dist/components/user/ssoMessageStream.d.ts +10 -0
- package/dist/components/user/ssoMessageStream.js +8 -0
- package/dist/components/user/ssoSession.d.ts +5 -0
- package/dist/components/user/ssoSession.js +5 -0
- package/dist/components/user/storage.d.ts +27 -0
- package/dist/components/user/storage.js +143 -0
- package/dist/components/user/types.d.ts +6 -0
- package/dist/components/user/types.js +1 -0
- package/dist/components/user/userSessionStorage.d.ts +20 -0
- package/dist/components/user/userSessionStorage.js +24 -0
- package/dist/components/user.d.ts +74 -0
- package/dist/components/user.js +188 -0
- package/dist/constants.d.ts +2 -1
- package/dist/constants.js +5 -1
- package/dist/helpers/abortError.d.ts +4 -0
- package/dist/helpers/abortError.js +8 -0
- package/dist/helpers/callbackRaceResolver.d.ts +1 -0
- package/dist/helpers/callbackRaceResolver.js +17 -0
- package/dist/helpers/result.d.ts +12 -0
- package/dist/helpers/result.js +15 -0
- package/dist/helpers/result.spec.d.ts +1 -0
- package/dist/helpers/result.spec.js +23 -0
- package/dist/helpers/utils.d.ts +2 -1
- package/dist/helpers/utils.js +11 -2
- package/dist/index.d.ts +2 -1
- package/dist/modules/crypto.d.ts +5 -2
- package/dist/modules/crypto.js +16 -5
- package/dist/modules/secretStorage.d.ts +7 -9
- package/dist/modules/secretStorage.js +20 -33
- package/dist/modules/session/helpers.d.ts +5 -0
- package/dist/modules/session/helpers.js +29 -0
- package/dist/modules/session/session.d.ts +12 -0
- package/dist/modules/session/session.js +46 -0
- package/dist/modules/session/types.d.ts +12 -0
- package/dist/modules/session/types.js +1 -0
- package/dist/modules/signIn.d.ts +32 -11
- package/dist/modules/signIn.js +95 -95
- package/dist/modules/state.d.ts +16 -0
- package/dist/modules/state.js +50 -0
- package/dist/modules/statementStore.d.ts +10 -11
- package/dist/modules/statementStore.js +14 -14
- package/dist/modules/statementTopic.d.ts +34 -0
- package/dist/modules/statementTopic.js +46 -0
- package/dist/modules/storageView.d.ts +25 -0
- package/dist/modules/storageView.js +51 -0
- package/dist/modules/syncStorage.d.ts +25 -0
- package/dist/modules/syncStorage.js +76 -0
- package/dist/modules/transport/codec.d.ts +24 -0
- package/dist/modules/transport/codec.js +36 -0
- package/dist/modules/transport/crypto.d.ts +2 -0
- package/dist/modules/transport/crypto.js +18 -0
- package/dist/modules/transport/transport.d.ts +27 -0
- package/dist/modules/transport/transport.js +56 -0
- package/dist/modules/user.d.ts +67 -0
- package/dist/modules/user.js +188 -0
- package/dist/modules/userManager.d.ts +15 -0
- package/dist/modules/userManager.js +105 -0
- package/dist/modules/userStorage.d.ts +19 -0
- package/dist/modules/userStorage.js +108 -0
- package/dist/modules/userStore.d.ts +15 -0
- package/dist/modules/userStore.js +105 -0
- package/dist/papp.d.ts +8 -7
- package/dist/papp.js +19 -31
- package/dist/structs.d.ts +10 -10
- package/dist/structs.js +17 -13
- package/dist/types.d.ts +1 -1
- package/package.json +8 -6
package/dist/modules/signIn.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { StatementAdapter } from '../adapters/statement/types.js';
|
|
2
2
|
import type { StorageAdapter } from '../adapters/storage/types.js';
|
|
3
|
-
import type {
|
|
3
|
+
import type { Result } from '../helpers/result.js';
|
|
4
4
|
import type { EncrPublicKey, SsPublicKey } from './crypto.js';
|
|
5
|
+
import type { User } from './userManager.js';
|
|
5
6
|
export declare const HandshakeData: import("scale-ts").Codec<{
|
|
6
7
|
tag: "V1";
|
|
7
8
|
value: [SsPublicKey, EncrPublicKey, string];
|
|
@@ -23,24 +24,44 @@ export type SignInStatus = {
|
|
|
23
24
|
message: string;
|
|
24
25
|
} | {
|
|
25
26
|
step: 'finished';
|
|
26
|
-
|
|
27
|
-
pappAccountId: string;
|
|
28
|
-
};
|
|
29
|
-
export type SignInResult = {
|
|
30
|
-
sessionTopic: SessionTopic;
|
|
31
|
-
pappAccountId: string;
|
|
27
|
+
user: User;
|
|
32
28
|
};
|
|
33
29
|
type Params = {
|
|
30
|
+
/**
|
|
31
|
+
* Host app Id.
|
|
32
|
+
* CAUTION! This value should be stable.
|
|
33
|
+
*/
|
|
34
34
|
appId: string;
|
|
35
|
+
/**
|
|
36
|
+
* URL for additional metadata that will be displayed during pairing process.
|
|
37
|
+
* Content of provided json shound be
|
|
38
|
+
* ```ts
|
|
39
|
+
* interface Metadata {
|
|
40
|
+
* name: string;
|
|
41
|
+
* icon: string; // url for icon. Icon should be a rasterized image with min size 256x256 px.
|
|
42
|
+
* }
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
35
45
|
metadata: string;
|
|
36
46
|
statements: StatementAdapter;
|
|
37
47
|
storage: StorageAdapter;
|
|
38
48
|
};
|
|
39
49
|
export declare function createSignInFlow({ appId, metadata, statements, storage }: Params): {
|
|
40
|
-
|
|
41
|
-
|
|
50
|
+
signInStatus: {
|
|
51
|
+
read(): SignInStatus;
|
|
52
|
+
write(value: SignInStatus): void;
|
|
53
|
+
reset(): void;
|
|
54
|
+
subscribe(fn: (value: SignInStatus) => void): import("nanoevents").Unsubscribe;
|
|
55
|
+
};
|
|
56
|
+
users: {
|
|
57
|
+
readSelectedUser(): Promise<Result<User | null>>;
|
|
58
|
+
readUser(accountId: string): Promise<Result<User | null>>;
|
|
59
|
+
createUser(user: User): Promise<Result<User>>;
|
|
60
|
+
removeUser(accountId: string): Promise<Result<void, Error>>;
|
|
61
|
+
readAccounts(): Promise<Result<string[], Error>>;
|
|
62
|
+
selectAccount(accountId: string): Promise<Result<void, Error>>;
|
|
63
|
+
};
|
|
64
|
+
signIn(): Promise<Result<User | null>>;
|
|
42
65
|
abortSignIn(): void;
|
|
43
|
-
getSignInStatus(): SignInStatus;
|
|
44
|
-
onStatusChange(callback: (status: SignInStatus) => void): import("nanoevents").Unsubscribe;
|
|
45
66
|
};
|
|
46
67
|
export {};
|
package/dist/modules/signIn.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { toHex } from '@polkadot-api/utils';
|
|
2
|
-
import { createNanoEvents } from 'nanoevents';
|
|
3
2
|
import { Bytes, Enum, Tuple, str } from 'scale-ts';
|
|
4
|
-
import {
|
|
3
|
+
import { err, fromPromise, ok, seq } from '../helpers/result.js';
|
|
4
|
+
import { isAbortError, toError } from '../helpers/utils.js';
|
|
5
5
|
import { ENCR_SECRET_SEED_SIZE, EncrPubKey, SS_SECRET_SEED_SIZE, SsPubKey, createEncrSecret, createRandomSeed, createSharedSecret, createSsSecret, createSymmetricKey, decrypt, getEncrPub, getSsPub, khash, mergeBytes, stringToBytes, } from './crypto.js';
|
|
6
6
|
import { createSecretStorage } from './secretStorage.js';
|
|
7
7
|
import { createSession } from './statementStore.js';
|
|
8
|
+
import { createSyncStorage } from './syncStorage.js';
|
|
9
|
+
import { createUserManager } from './userManager.js';
|
|
8
10
|
// codecs
|
|
9
11
|
export const HandshakeData = Enum({
|
|
10
12
|
V1: Tuple(SsPubKey, EncrPubKey, str),
|
|
@@ -15,102 +17,76 @@ export const HandshakeResponsePayload = Enum({
|
|
|
15
17
|
});
|
|
16
18
|
export const HandshakeResponseSensitiveData = Tuple(Bytes(65), Bytes(32));
|
|
17
19
|
export function createSignInFlow({ appId, metadata, statements, storage }) {
|
|
20
|
+
const userManager = createUserManager(appId, storage);
|
|
18
21
|
const secretStorage = createSecretStorage(appId, storage);
|
|
19
|
-
const
|
|
20
|
-
let signInStatus = { step: 'none' };
|
|
21
|
-
events.on('status', status => {
|
|
22
|
-
signInStatus = status;
|
|
23
|
-
});
|
|
22
|
+
const signInStatus = createSyncStorage({ step: 'none' });
|
|
24
23
|
let signInPromise = null;
|
|
25
24
|
let abort = null;
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
25
|
+
async function handshake(signal) {
|
|
26
|
+
signInStatus.write({ step: 'initial' });
|
|
27
|
+
const secrets = await getSecretKeys(appId, secretStorage);
|
|
28
|
+
return secrets.andThenPromise(async ({ ssPublicKey, encrPublicKey, encrSecret }) => {
|
|
29
|
+
const handshakeTopic = createHandshakeTopic({ encrPublicKey, ssPublicKey });
|
|
30
|
+
const handshakePayload = createHandshakePayloadV1({ ssPublicKey, encrPublicKey, metadata });
|
|
31
|
+
signInStatus.write({ step: 'pairing', payload: createDeeplink(handshakePayload) });
|
|
32
|
+
const statementStoreResponse = fromPromise(waitForStatements(statements, handshakeTopic, signal, (statements, resolve) => {
|
|
33
|
+
for (const statement of [...statements].reverse()) {
|
|
34
|
+
if (!statement.data)
|
|
35
|
+
continue;
|
|
36
|
+
const { sessionTopic, accountId } = retrieveSessionTopic({
|
|
37
|
+
payload: statement.data.asBytes(),
|
|
38
|
+
encrSecret,
|
|
39
|
+
ssPublicKey,
|
|
34
40
|
});
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
pappAccountId: existingPappAccountId,
|
|
38
|
-
};
|
|
41
|
+
resolve({ sessionTopic, accountId: toHex(accountId) });
|
|
42
|
+
break;
|
|
39
43
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
44
|
+
}), toError);
|
|
45
|
+
return statementStoreResponse
|
|
46
|
+
.then(x => x.andThenPromise(userManager.createUser))
|
|
47
|
+
.then(async (result) => result
|
|
48
|
+
.map(user => {
|
|
49
|
+
signInStatus.write({ step: 'finished', user });
|
|
50
|
+
return user;
|
|
51
|
+
})
|
|
52
|
+
.orElse(e => {
|
|
53
|
+
const error = toError(e);
|
|
54
|
+
if (isAbortError(error)) {
|
|
55
|
+
signInStatus.write({ step: 'none' });
|
|
56
|
+
return ok(null);
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
signInStatus.write({ step: 'error', message: error.message });
|
|
60
|
+
return err(error);
|
|
61
|
+
}
|
|
62
|
+
}));
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
const signInFlow = {
|
|
66
|
+
signInStatus,
|
|
67
|
+
users: userManager,
|
|
43
68
|
async signIn() {
|
|
44
69
|
if (signInPromise) {
|
|
45
70
|
return signInPromise;
|
|
46
71
|
}
|
|
47
72
|
abort = new AbortController();
|
|
48
|
-
|
|
49
|
-
signInPromise = signInFlow
|
|
50
|
-
.getSignedUser()
|
|
51
|
-
.then(async (signedIn) => {
|
|
52
|
-
if (signedIn) {
|
|
53
|
-
events.emit('status', {
|
|
54
|
-
step: 'finished',
|
|
55
|
-
sessionTopic: signedIn.sessionTopic,
|
|
56
|
-
pappAccountId: signedIn.pappAccountId,
|
|
57
|
-
});
|
|
58
|
-
return signedIn;
|
|
59
|
-
}
|
|
60
|
-
const { ssPublicKey, encrPublicKey, encrSecret } = await getSecretKeys(appId, secretStorage);
|
|
61
|
-
const handshakeTopic = createHandshakeTopic({ encrPublicKey, ssPublicKey });
|
|
62
|
-
const handshakePayload = createHandshakePayload({ ssPublicKey, encrPublicKey, metadata });
|
|
63
|
-
events.emit('status', { step: 'pairing', payload: createDeeplink(handshakePayload) });
|
|
64
|
-
return waitForStatements(statements, handshakeTopic, abort?.signal ?? null, (statements, resolve) => {
|
|
65
|
-
for (const statement of [...statements].reverse()) {
|
|
66
|
-
if (!statement.data)
|
|
67
|
-
continue;
|
|
68
|
-
const { sessionTopic, pappAccountId } = retrieveSessionTopic({
|
|
69
|
-
payload: statement.data.asBytes(),
|
|
70
|
-
encrSecret,
|
|
71
|
-
ssPublicKey,
|
|
72
|
-
});
|
|
73
|
-
resolve({ sessionTopic, pappAccountId: toHex(pappAccountId) });
|
|
74
|
-
break;
|
|
75
|
-
}
|
|
76
|
-
});
|
|
77
|
-
})
|
|
78
|
-
.then(async ({ sessionTopic, pappAccountId }) => {
|
|
79
|
-
await secretStorage.writeSessionTopic(sessionTopic);
|
|
80
|
-
await secretStorage.writePappAccountId(pappAccountId);
|
|
81
|
-
events.emit('status', { step: 'finished', sessionTopic, pappAccountId });
|
|
82
|
-
return { sessionTopic, pappAccountId };
|
|
83
|
-
})
|
|
84
|
-
.catch(e => {
|
|
85
|
-
if (isAbortError(e)) {
|
|
86
|
-
events.emit('status', { step: 'none' });
|
|
87
|
-
return null;
|
|
88
|
-
}
|
|
89
|
-
events.emit('status', { step: 'error', message: e.message });
|
|
90
|
-
throw e;
|
|
91
|
-
});
|
|
73
|
+
signInPromise = handshake(abort.signal);
|
|
92
74
|
return signInPromise;
|
|
93
75
|
},
|
|
94
76
|
abortSignIn() {
|
|
95
77
|
if (abort) {
|
|
96
78
|
signInPromise = null;
|
|
97
|
-
|
|
79
|
+
signInStatus.reset();
|
|
98
80
|
abort.abort();
|
|
99
81
|
}
|
|
100
82
|
},
|
|
101
|
-
getSignInStatus() {
|
|
102
|
-
return signInStatus;
|
|
103
|
-
},
|
|
104
|
-
onStatusChange(callback) {
|
|
105
|
-
return events.on('status', callback);
|
|
106
|
-
},
|
|
107
83
|
};
|
|
108
84
|
return signInFlow;
|
|
109
85
|
}
|
|
110
86
|
function createHandshakeTopic({ encrPublicKey, ssPublicKey, }) {
|
|
111
87
|
return khash(ssPublicKey, mergeBytes(encrPublicKey, stringToBytes('topic')));
|
|
112
88
|
}
|
|
113
|
-
function
|
|
89
|
+
function createHandshakePayloadV1({ encrPublicKey, ssPublicKey, metadata, }) {
|
|
114
90
|
return HandshakeData.enc({
|
|
115
91
|
tag: 'V1',
|
|
116
92
|
value: [ssPublicKey, encrPublicKey, metadata],
|
|
@@ -132,7 +108,6 @@ function retrieveSessionTopic({ payload, encrSecret, ssPublicKey, }) {
|
|
|
132
108
|
const { encrypted, tmpKey } = parseHandshakePayload(payload);
|
|
133
109
|
const symmetricKey = createSymmetricKey(createSharedSecret(encrSecret, tmpKey));
|
|
134
110
|
const decrypted = decrypt(symmetricKey, encrypted);
|
|
135
|
-
console.log('decrypted', decrypted.length, 65 + 32); // true
|
|
136
111
|
const [pappEncrPublicKey, userPublicKey] = HandshakeResponseSensitiveData.dec(decrypted);
|
|
137
112
|
const sharedSecret = createSharedSecret(encrSecret, pappEncrPublicKey);
|
|
138
113
|
const session = createSession({
|
|
@@ -140,29 +115,48 @@ function retrieveSessionTopic({ payload, encrSecret, ssPublicKey, }) {
|
|
|
140
115
|
accountA: ssPublicKey,
|
|
141
116
|
accountB: pappEncrPublicKey,
|
|
142
117
|
});
|
|
143
|
-
console.log('userPublicKey', userPublicKey.length, toHex(userPublicKey));
|
|
144
|
-
console.log('sessionTopic', session.a.length, toHex(session.a));
|
|
145
118
|
return {
|
|
146
|
-
|
|
119
|
+
accountId: userPublicKey,
|
|
147
120
|
sessionTopic: session.a,
|
|
148
121
|
};
|
|
149
122
|
}
|
|
150
|
-
async function
|
|
151
|
-
|
|
152
|
-
|
|
123
|
+
async function getSsKeys(appId, secretStorage) {
|
|
124
|
+
return (await secretStorage.readSsSecret())
|
|
125
|
+
.andThenPromise(async (ssSecret) => {
|
|
126
|
+
if (ssSecret) {
|
|
127
|
+
return ok(ssSecret);
|
|
128
|
+
}
|
|
153
129
|
const seed = createRandomSeed(appId, SS_SECRET_SEED_SIZE);
|
|
154
|
-
|
|
155
|
-
await secretStorage.writeSsSecret(
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
130
|
+
const newSsSecret = createSsSecret(seed);
|
|
131
|
+
const write = await secretStorage.writeSsSecret(newSsSecret);
|
|
132
|
+
return write.map(() => newSsSecret);
|
|
133
|
+
})
|
|
134
|
+
.then(x => x.map(ssSecret => ({
|
|
135
|
+
ssSecret: ssSecret,
|
|
136
|
+
ssPublicKey: getSsPub(ssSecret),
|
|
137
|
+
})));
|
|
138
|
+
}
|
|
139
|
+
async function getEncrKeys(appId, secretStorage) {
|
|
140
|
+
return (await secretStorage.readEncrSecret())
|
|
141
|
+
.andThenPromise(async (encrSecret) => {
|
|
142
|
+
if (encrSecret) {
|
|
143
|
+
return ok(encrSecret);
|
|
144
|
+
}
|
|
159
145
|
const seed = createRandomSeed(appId, ENCR_SECRET_SEED_SIZE);
|
|
160
|
-
|
|
161
|
-
await secretStorage.writeEncrSecret(
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
146
|
+
const newEncrSecret = createEncrSecret(seed);
|
|
147
|
+
const write = await secretStorage.writeEncrSecret(newEncrSecret);
|
|
148
|
+
return write.map(() => newEncrSecret);
|
|
149
|
+
})
|
|
150
|
+
.then(x => x.map(encrSecret => ({
|
|
151
|
+
encrSecret,
|
|
152
|
+
encrPublicKey: getEncrPub(encrSecret),
|
|
153
|
+
})));
|
|
154
|
+
}
|
|
155
|
+
async function getSecretKeys(appId, secretStorage) {
|
|
156
|
+
return seq(await getSsKeys(appId, secretStorage), await getEncrKeys(appId, secretStorage)).map(([ss, encr]) => ({
|
|
157
|
+
...ss,
|
|
158
|
+
...encr,
|
|
159
|
+
}));
|
|
166
160
|
}
|
|
167
161
|
function createDeeplink(payload) {
|
|
168
162
|
return `polkadotapp://pair?handshake=${toHex(payload)}`;
|
|
@@ -179,10 +173,16 @@ function waitForStatements(transport, topic, abortSignal, callback) {
|
|
|
179
173
|
reject(e);
|
|
180
174
|
}
|
|
181
175
|
}
|
|
182
|
-
|
|
176
|
+
try {
|
|
177
|
+
callback(statements, value => {
|
|
178
|
+
unsubscribe();
|
|
179
|
+
resolve(value);
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
catch (e) {
|
|
183
183
|
unsubscribe();
|
|
184
|
-
|
|
185
|
-
}
|
|
184
|
+
reject(e);
|
|
185
|
+
}
|
|
186
186
|
});
|
|
187
187
|
});
|
|
188
188
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
type State<T> = ReturnType<typeof createState<T>>;
|
|
2
|
+
export declare function createState<T>(initial: T): {
|
|
3
|
+
read(): T;
|
|
4
|
+
write(value: T): T;
|
|
5
|
+
reset(): void;
|
|
6
|
+
subscribe(fn: (value: T) => void): () => void;
|
|
7
|
+
onFirstSubscribe(callback: VoidFunction): import("nanoevents").Unsubscribe;
|
|
8
|
+
onLastUnsubscribe(callback: VoidFunction): import("nanoevents").Unsubscribe;
|
|
9
|
+
};
|
|
10
|
+
export declare function readonly<T>(state: State<T>): {
|
|
11
|
+
read: () => T;
|
|
12
|
+
subscribe: (fn: (value: T) => void) => () => void;
|
|
13
|
+
onFirstSubscribe: (callback: VoidFunction) => import("nanoevents").Unsubscribe;
|
|
14
|
+
onLastUnsubscribe: (callback: VoidFunction) => import("nanoevents").Unsubscribe;
|
|
15
|
+
};
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { createNanoEvents } from 'nanoevents';
|
|
2
|
+
export function createState(initial) {
|
|
3
|
+
const events = createNanoEvents();
|
|
4
|
+
let currentValue = initial;
|
|
5
|
+
return {
|
|
6
|
+
read() {
|
|
7
|
+
return currentValue;
|
|
8
|
+
},
|
|
9
|
+
write(value) {
|
|
10
|
+
if (currentValue !== value) {
|
|
11
|
+
currentValue = value;
|
|
12
|
+
events.emit('value', value);
|
|
13
|
+
}
|
|
14
|
+
return value;
|
|
15
|
+
},
|
|
16
|
+
reset() {
|
|
17
|
+
if (currentValue !== initial) {
|
|
18
|
+
currentValue = initial;
|
|
19
|
+
events.emit('value', initial);
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
subscribe(fn) {
|
|
23
|
+
if (!events.events.value || events.events.value.length === 0) {
|
|
24
|
+
events.emit('first');
|
|
25
|
+
}
|
|
26
|
+
const unsubscribe = events.on('value', fn);
|
|
27
|
+
fn(currentValue);
|
|
28
|
+
return () => {
|
|
29
|
+
unsubscribe();
|
|
30
|
+
if (!events.events.value || events.events.value.length === 0) {
|
|
31
|
+
events.emit('last');
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
},
|
|
35
|
+
onFirstSubscribe(callback) {
|
|
36
|
+
return events.on('first', callback);
|
|
37
|
+
},
|
|
38
|
+
onLastUnsubscribe(callback) {
|
|
39
|
+
return events.on('last', callback);
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
export function readonly(state) {
|
|
44
|
+
return {
|
|
45
|
+
read: state.read,
|
|
46
|
+
subscribe: state.subscribe,
|
|
47
|
+
onFirstSubscribe: state.onFirstSubscribe,
|
|
48
|
+
onLastUnsubscribe: state.onLastUnsubscribe,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { UserSession } from '../components/user/userSessionStorage.js';
|
|
2
2
|
import type { SsSecret } from './crypto.js';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
a: Uint8Array<ArrayBufferLike>;
|
|
11
|
-
b: Uint8Array<ArrayBufferLike>;
|
|
3
|
+
import type { Account } from './session/types.js';
|
|
4
|
+
export declare function createUserSession(hostAccount: Account, peerAccount: Account): UserSession;
|
|
5
|
+
type StatementPayload = {
|
|
6
|
+
priority: number;
|
|
7
|
+
channel: Uint8Array;
|
|
8
|
+
topics: Uint8Array[];
|
|
9
|
+
data: Uint8Array;
|
|
12
10
|
};
|
|
13
|
-
export declare function createStatement(secret: SsSecret, payload:
|
|
11
|
+
export declare function createStatement(secret: SsSecret, payload: StatementPayload): Promise<import("@polkadot-api/sdk-statement").SignedStatement>;
|
|
12
|
+
export {};
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
import { getStatementSigner } from '@polkadot-api/sdk-statement';
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const pinSeparator = stringToBytes('/');
|
|
7
|
-
function makePin(pin) {
|
|
8
|
-
return pin ? mergeBytes(pinSeparator, stringToBytes(pin)) : pinSeparator;
|
|
9
|
-
}
|
|
10
|
-
const accountASessionParams = mergeBytes(sessionPrefix, accountA, accountB, makePin(pinA), makePin(pinB));
|
|
11
|
-
const accountBSessionParams = mergeBytes(sessionPrefix, accountB, accountA, makePin(pinB), makePin(pinA));
|
|
2
|
+
import { Binary } from '@polkadot-api/substrate-bindings';
|
|
3
|
+
import { nanoid } from 'nanoid';
|
|
4
|
+
import { getSsPub, signWithSsSecret } from './crypto.js';
|
|
5
|
+
export function createUserSession(hostAccount, peerAccount) {
|
|
12
6
|
return {
|
|
13
|
-
|
|
14
|
-
|
|
7
|
+
id: nanoid(12),
|
|
8
|
+
peer: peerAccount,
|
|
9
|
+
host: hostAccount,
|
|
15
10
|
};
|
|
16
11
|
}
|
|
17
12
|
export function createStatement(secret, payload) {
|
|
18
|
-
const signer = getStatementSigner(getSsPub(secret), 'sr25519', data =>
|
|
19
|
-
return signer.sign(
|
|
13
|
+
const signer = getStatementSigner(getSsPub(secret), 'sr25519', data => signWithSsSecret(secret, data));
|
|
14
|
+
return signer.sign({
|
|
15
|
+
priority: payload.priority,
|
|
16
|
+
channel: Binary.fromBytes(payload.channel),
|
|
17
|
+
topics: payload.topics.map(Binary.fromBytes),
|
|
18
|
+
data: Binary.fromBytes(payload.data),
|
|
19
|
+
});
|
|
20
20
|
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { Statement } from '@polkadot-api/sdk-statement';
|
|
2
|
+
import type { StatementAdapter } from '../adapters/statement/types.js';
|
|
3
|
+
import type { StorageAdapter } from '../adapters/storage/types.js';
|
|
4
|
+
import type { Callback } from '../types.js';
|
|
5
|
+
import type { SsSecret } from './crypto.js';
|
|
6
|
+
export declare function createSession({ sharedSecret, accountA, accountB, pinA, pinB, }: {
|
|
7
|
+
sharedSecret: Uint8Array;
|
|
8
|
+
accountA: Uint8Array;
|
|
9
|
+
accountB: Uint8Array;
|
|
10
|
+
pinA?: string;
|
|
11
|
+
pinB?: string;
|
|
12
|
+
}): {
|
|
13
|
+
a: Uint8Array<ArrayBufferLike>;
|
|
14
|
+
b: Uint8Array<ArrayBufferLike>;
|
|
15
|
+
};
|
|
16
|
+
export declare function createRequestChannel(session: Uint8Array): Uint8Array<ArrayBufferLike>;
|
|
17
|
+
export declare function createResponseChannel(session: Uint8Array): Uint8Array<ArrayBufferLike>;
|
|
18
|
+
type StatementPayload = {
|
|
19
|
+
priority: number;
|
|
20
|
+
channel: Uint8Array;
|
|
21
|
+
topics: Uint8Array[];
|
|
22
|
+
data: Uint8Array;
|
|
23
|
+
};
|
|
24
|
+
export declare function createStatement(secret: SsSecret, payload: StatementPayload): Promise<import("@polkadot-api/sdk-statement").SignedStatement>;
|
|
25
|
+
type Params = {
|
|
26
|
+
topic: Uint8Array;
|
|
27
|
+
statement: StatementAdapter;
|
|
28
|
+
storage: StorageAdapter;
|
|
29
|
+
};
|
|
30
|
+
export declare function createStatementTopic({ topic, statement }: Params): {
|
|
31
|
+
subscribe(callback: Callback<Statement, boolean>): VoidFunction;
|
|
32
|
+
submit(): void;
|
|
33
|
+
};
|
|
34
|
+
export {};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { getStatementSigner } from '@polkadot-api/sdk-statement';
|
|
2
|
+
import { Binary } from '@polkadot-api/substrate-bindings';
|
|
3
|
+
import { getSsPub, khash, mergeBytes, signWithSsSecret, stringToBytes } from './crypto.js';
|
|
4
|
+
export function createSession({ sharedSecret, accountA, accountB, pinA, pinB, }) {
|
|
5
|
+
const sessionPrefix = stringToBytes('session');
|
|
6
|
+
const pinSeparator = stringToBytes('/');
|
|
7
|
+
function makePin(pin) {
|
|
8
|
+
return pin ? mergeBytes(pinSeparator, stringToBytes(pin)) : pinSeparator;
|
|
9
|
+
}
|
|
10
|
+
const accountASessionParams = mergeBytes(sessionPrefix, accountA, accountB, makePin(pinA), makePin(pinB));
|
|
11
|
+
const accountBSessionParams = mergeBytes(sessionPrefix, accountB, accountA, makePin(pinB), makePin(pinA));
|
|
12
|
+
return {
|
|
13
|
+
a: khash(sharedSecret, accountASessionParams),
|
|
14
|
+
b: khash(sharedSecret, accountBSessionParams),
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
export function createRequestChannel(session) {
|
|
18
|
+
return khash(session, stringToBytes('request'));
|
|
19
|
+
}
|
|
20
|
+
export function createResponseChannel(session) {
|
|
21
|
+
return khash(session, stringToBytes('response'));
|
|
22
|
+
}
|
|
23
|
+
export function createStatement(secret, payload) {
|
|
24
|
+
const signer = getStatementSigner(getSsPub(secret), 'sr25519', data => signWithSsSecret(secret, data));
|
|
25
|
+
return signer.sign({
|
|
26
|
+
priority: payload.priority,
|
|
27
|
+
channel: Binary.fromBytes(payload.channel),
|
|
28
|
+
topics: payload.topics.map(Binary.fromBytes),
|
|
29
|
+
data: Binary.fromBytes(payload.data),
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
export function createStatementTopic({ topic, statement }) {
|
|
33
|
+
return {
|
|
34
|
+
subscribe(callback) {
|
|
35
|
+
return statement.subscribeStatements([topic], statements => {
|
|
36
|
+
for (const statement of statements) {
|
|
37
|
+
const processed = callback(statement);
|
|
38
|
+
if (processed) {
|
|
39
|
+
// TODO save anywhere
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
},
|
|
44
|
+
submit() { },
|
|
45
|
+
};
|
|
46
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { ResultAsync } from 'neverthrow';
|
|
2
|
+
import type { StorageAdapter } from '../adapters/storage/types.js';
|
|
3
|
+
type Params<T> = {
|
|
4
|
+
storage: StorageAdapter;
|
|
5
|
+
key: string;
|
|
6
|
+
initial: T;
|
|
7
|
+
autosync?: boolean;
|
|
8
|
+
from(value: string): T;
|
|
9
|
+
to(value: T): string | null;
|
|
10
|
+
};
|
|
11
|
+
export declare function storageView<T>({ storage, initial, key, from, to, autosync }: Params<T>): {
|
|
12
|
+
read(): ResultAsync<T, Error>;
|
|
13
|
+
write(value: T): ResultAsync<T, Error> | ResultAsync<null, Error>;
|
|
14
|
+
clear(): ResultAsync<void, Error>;
|
|
15
|
+
subscribe(fn: (value: T) => void): () => void;
|
|
16
|
+
};
|
|
17
|
+
export declare function storageListView<T>(params: Params<T[]>): {
|
|
18
|
+
add(value: T): ResultAsync<T, Error>;
|
|
19
|
+
mutate(fn: (value: T[]) => T[]): ResultAsync<T[], Error>;
|
|
20
|
+
read(): ResultAsync<T[], Error>;
|
|
21
|
+
write(value: T[]): ResultAsync<T[], Error> | ResultAsync<null, Error>;
|
|
22
|
+
clear(): ResultAsync<void, Error>;
|
|
23
|
+
subscribe(fn: (value: T[]) => void): () => void;
|
|
24
|
+
};
|
|
25
|
+
export {};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { okAsync } from 'neverthrow';
|
|
2
|
+
import { nonNullable } from '../helpers/utils.js';
|
|
3
|
+
import { createState } from './state.js';
|
|
4
|
+
export function storageView({ storage, initial, key, from, to, autosync = true }) {
|
|
5
|
+
const state = createState(to(initial));
|
|
6
|
+
const enhancedStorage = {
|
|
7
|
+
read() {
|
|
8
|
+
return storage
|
|
9
|
+
.read(key)
|
|
10
|
+
.map(state.write)
|
|
11
|
+
.map(x => (nonNullable(x) ? from(x) : initial));
|
|
12
|
+
},
|
|
13
|
+
write(value) {
|
|
14
|
+
const data = to(value);
|
|
15
|
+
if (data !== null) {
|
|
16
|
+
return storage
|
|
17
|
+
.write(key, data)
|
|
18
|
+
.map(() => state.write(data))
|
|
19
|
+
.map(() => value);
|
|
20
|
+
}
|
|
21
|
+
return okAsync(null);
|
|
22
|
+
},
|
|
23
|
+
clear() {
|
|
24
|
+
return storage.clear(key).map(() => state.reset());
|
|
25
|
+
},
|
|
26
|
+
subscribe(fn) {
|
|
27
|
+
return state.subscribe(x => fn(nonNullable(x) ? from(x) : initial));
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
if (autosync) {
|
|
31
|
+
enhancedStorage.read();
|
|
32
|
+
}
|
|
33
|
+
state.onFirstSubscribe(() => enhancedStorage.read());
|
|
34
|
+
return enhancedStorage;
|
|
35
|
+
}
|
|
36
|
+
export function storageListView(params) {
|
|
37
|
+
const view = storageView(params);
|
|
38
|
+
const listView = {
|
|
39
|
+
...view,
|
|
40
|
+
add(value) {
|
|
41
|
+
return listView.mutate(list => list.concat(value)).map(() => value);
|
|
42
|
+
},
|
|
43
|
+
mutate(fn) {
|
|
44
|
+
return listView.read().andThen(list => {
|
|
45
|
+
const result = fn(list);
|
|
46
|
+
return listView.write(result).map(() => result);
|
|
47
|
+
});
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
return listView;
|
|
51
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { StorageAdapter } from '../adapters/storage/types.js';
|
|
2
|
+
export declare function createSyncStorage<T>(initial: T): {
|
|
3
|
+
touched(): boolean;
|
|
4
|
+
read(): T;
|
|
5
|
+
write(value: T): T;
|
|
6
|
+
reset(): void;
|
|
7
|
+
subscribe(fn: (value: T) => void): () => void;
|
|
8
|
+
onFirstSubscribe(callback: VoidFunction): import("nanoevents").Unsubscribe;
|
|
9
|
+
onLastUnsubscribe(callback: VoidFunction): import("nanoevents").Unsubscribe;
|
|
10
|
+
};
|
|
11
|
+
type ReactiveStorageParams<T> = {
|
|
12
|
+
storage: StorageAdapter;
|
|
13
|
+
key: string;
|
|
14
|
+
initial: T;
|
|
15
|
+
autosync: boolean;
|
|
16
|
+
from(value: string): T;
|
|
17
|
+
to(value: T): string | null;
|
|
18
|
+
};
|
|
19
|
+
export declare function reactiveStorage<T>({ storage, initial, key, autosync, from, to }: ReactiveStorageParams<T>): {
|
|
20
|
+
read(): Promise<import("../helpers/result.js").Result<T, Error>>;
|
|
21
|
+
write(value: T): Promise<import("../helpers/result.js").Result<null, Error> | import("../helpers/result.js").Result<T, Error>>;
|
|
22
|
+
clear(): Promise<import("../helpers/result.js").Result<void, Error>>;
|
|
23
|
+
subscribe(fn: (value: T | null) => void): () => void;
|
|
24
|
+
};
|
|
25
|
+
export {};
|