@pomade/core 0.0.11 → 0.1.0-pre.1

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/client.d.ts CHANGED
@@ -1,8 +1,7 @@
1
- import { Maybe } from "@welshman/lib";
2
- import type { SignedEvent, StampedEvent } from "@welshman/util";
1
+ import type { StampedEvent, SignedEvent } from "@welshman/util";
3
2
  import type { GroupPackage } from "@frostr/bifrost";
4
- import { RPC, WithEvent } from "./rpc.js";
5
- import { LoginOptions, LoginResult, RecoveryOptions, RecoveryResult, RecoverySetupResult, RegisterResult, SessionDeleteResult, SessionListResult, SignResult } from "./message.js";
3
+ import { RPC } from "./rpc.js";
4
+ import { Message, LoginStartResponse, RecoveryStartResponse } from "./message.js";
6
5
  export type ClientOptions = {
7
6
  group: GroupPackage;
8
7
  secret: string;
@@ -11,7 +10,7 @@ export type ClientOptions = {
11
10
  export type ClientOptionsResult<T> = {
12
11
  ok: boolean;
13
12
  options: [string, string[]][];
14
- messages: Maybe<T>[];
13
+ messages: Message<T>[];
15
14
  clientSecret: string;
16
15
  };
17
16
  export declare class Client {
@@ -20,13 +19,15 @@ export declare class Client {
20
19
  group: GroupPackage;
21
20
  userPubkey: string;
22
21
  constructor(options: ClientOptions);
23
- stop(): void;
24
22
  getPubkey(): Promise<string>;
25
- static _buildOptions<T extends WithEvent<LoginOptions | RecoveryOptions>>(clientSecret: string, messages: Maybe<T>[], threshold: "total" | "threshold"): ClientOptionsResult<T>;
23
+ static _buildOptions<T extends LoginStartResponse | RecoveryStartResponse>(clientSecret: string, messages: Message<T>[], threshold: "total" | "threshold"): ClientOptionsResult<T>;
26
24
  static _getKnownPeers(): string[];
27
25
  static register(threshold: number, n: number, userSecret: string, recovery?: boolean): Promise<{
28
26
  ok: boolean;
29
- messages: Maybe<RegisterResult>[];
27
+ messages: (Message<{
28
+ ok: boolean;
29
+ message: string;
30
+ }> | undefined)[];
30
31
  clientOptions: {
31
32
  peers: string[];
32
33
  group: GroupPackage;
@@ -35,43 +36,164 @@ export declare class Client {
35
36
  }>;
36
37
  setupRecovery(email: string, password: string): Promise<{
37
38
  ok: boolean;
38
- messages: Maybe<WithEvent<RecoverySetupResult>>[];
39
+ messages: Message<{
40
+ ok: boolean;
41
+ message: string;
42
+ }>[];
39
43
  }>;
40
44
  static requestChallenge(email: string, peers?: string[]): Promise<{
41
45
  ok: boolean;
42
46
  peersByPrefix: Map<string, string>;
43
47
  }>;
44
- static loginWithPassword(email: string, password: string): Promise<ClientOptionsResult<WithEvent<LoginOptions>>>;
45
- static loginWithChallenge(email: string, peersByPrefix: Map<string, string>, otps: string[]): Promise<ClientOptionsResult<WithEvent<LoginOptions>>>;
48
+ static loginWithPassword(email: string, password: string): Promise<ClientOptionsResult<{
49
+ ok: boolean;
50
+ message: string;
51
+ items?: {
52
+ pubkey: string;
53
+ client: string;
54
+ created_at: number;
55
+ last_activity: number;
56
+ threshold: number;
57
+ total: number;
58
+ idx: number;
59
+ email?: string | undefined;
60
+ }[] | undefined;
61
+ }>>;
62
+ static loginWithChallenge(email: string, peersByPrefix: Map<string, string>, otps: string[]): Promise<ClientOptionsResult<{
63
+ ok: boolean;
64
+ message: string;
65
+ items?: {
66
+ pubkey: string;
67
+ client: string;
68
+ created_at: number;
69
+ last_activity: number;
70
+ threshold: number;
71
+ total: number;
72
+ idx: number;
73
+ email?: string | undefined;
74
+ }[] | undefined;
75
+ }>>;
46
76
  static selectLogin(clientSecret: string, client: string, peers: string[]): Promise<{
47
77
  ok: boolean;
48
- messages: Maybe<WithEvent<LoginResult>>[];
78
+ messages: Message<{
79
+ ok: boolean;
80
+ message: string;
81
+ group?: {
82
+ commits: {
83
+ idx: number;
84
+ pubkey: string;
85
+ hidden_pn: string;
86
+ binder_pn: string;
87
+ }[];
88
+ group_pk: string;
89
+ threshold: number;
90
+ } | undefined;
91
+ }>[];
49
92
  clientOptions: ClientOptions | undefined;
50
93
  }>;
51
- static recoverWithPassword(email: string, password: string): Promise<ClientOptionsResult<WithEvent<RecoveryOptions>>>;
52
- static recoverWithChallenge(email: string, peersByPrefix: Map<string, string>, otps: string[]): Promise<ClientOptionsResult<WithEvent<RecoveryOptions>>>;
94
+ static recoverWithPassword(email: string, password: string): Promise<ClientOptionsResult<{
95
+ ok: boolean;
96
+ message: string;
97
+ items?: {
98
+ pubkey: string;
99
+ client: string;
100
+ created_at: number;
101
+ last_activity: number;
102
+ threshold: number;
103
+ total: number;
104
+ idx: number;
105
+ email?: string | undefined;
106
+ }[] | undefined;
107
+ }>>;
108
+ static recoverWithChallenge(email: string, peersByPrefix: Map<string, string>, otps: string[]): Promise<ClientOptionsResult<{
109
+ ok: boolean;
110
+ message: string;
111
+ items?: {
112
+ pubkey: string;
113
+ client: string;
114
+ created_at: number;
115
+ last_activity: number;
116
+ threshold: number;
117
+ total: number;
118
+ idx: number;
119
+ email?: string | undefined;
120
+ }[] | undefined;
121
+ }>>;
53
122
  static selectRecovery(clientSecret: string, client: string, peers: string[]): Promise<{
54
123
  ok: boolean;
55
- messages: Maybe<WithEvent<RecoveryResult>>[];
124
+ messages: Message<{
125
+ ok: boolean;
126
+ message: string;
127
+ share?: {
128
+ idx: number;
129
+ binder_sn: string;
130
+ hidden_sn: string;
131
+ seckey: string;
132
+ } | undefined;
133
+ group?: {
134
+ commits: {
135
+ idx: number;
136
+ pubkey: string;
137
+ hidden_pn: string;
138
+ binder_pn: string;
139
+ }[];
140
+ group_pk: string;
141
+ threshold: number;
142
+ } | undefined;
143
+ }>[];
56
144
  userSecret: string | undefined;
57
145
  }>;
58
146
  sign(stampedEvent: StampedEvent): Promise<{
59
147
  ok: boolean;
60
- messages: Maybe<WithEvent<SignResult>>[];
148
+ messages: Message<{
149
+ ok: boolean;
150
+ message: string;
151
+ result?: {
152
+ idx: number;
153
+ psigs: [string, string][];
154
+ pubkey: string;
155
+ sid: string;
156
+ } | undefined;
157
+ }>[];
61
158
  event: SignedEvent;
62
159
  } | {
63
160
  ok: boolean;
64
- messages: Maybe<WithEvent<SignResult>>[];
161
+ messages: Message<{
162
+ ok: boolean;
163
+ message: string;
164
+ result?: {
165
+ idx: number;
166
+ psigs: [string, string][];
167
+ pubkey: string;
168
+ sid: string;
169
+ } | undefined;
170
+ }>[];
65
171
  event?: undefined;
66
172
  }>;
67
173
  getConversationKey(ecdh_pk: string): Promise<string | undefined>;
68
174
  listSessions(): Promise<{
69
175
  ok: boolean;
70
- messages: Maybe<WithEvent<SessionListResult>>[];
176
+ messages: Message<{
177
+ ok: boolean;
178
+ message: string;
179
+ items: {
180
+ pubkey: string;
181
+ client: string;
182
+ created_at: number;
183
+ last_activity: number;
184
+ threshold: number;
185
+ total: number;
186
+ idx: number;
187
+ email?: string | undefined;
188
+ }[];
189
+ }>[];
71
190
  }>;
72
191
  deleteSession(client: string, peers: string[]): Promise<{
73
192
  ok: boolean;
74
- messages: Maybe<WithEvent<SessionDeleteResult>>[];
193
+ messages: Message<{
194
+ ok: boolean;
195
+ message: string;
196
+ }>[];
75
197
  }>;
76
198
  }
77
199
  //# sourceMappingURL=client.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,EAaN,MAAM,eAAe,CAAA;AAKtB,OAAO,KAAK,EAAC,WAAW,EAAE,YAAY,EAAC,MAAM,gBAAgB,CAAA;AAE7D,OAAO,KAAK,EAAC,YAAY,EAAc,MAAM,iBAAiB,CAAA;AAG9D,OAAO,EAAC,GAAG,EAAE,SAAS,EAAC,MAAM,UAAU,CAAA;AACvC,OAAO,EAsBL,YAAY,EACZ,WAAW,EACX,eAAe,EACf,cAAc,EACd,mBAAmB,EACnB,cAAc,EACd,mBAAmB,EACnB,iBAAiB,EACjB,UAAU,EACX,MAAM,cAAc,CAAA;AAErB,MAAM,MAAM,aAAa,GAAG;IAC1B,KAAK,EAAE,YAAY,CAAA;IACnB,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,EAAE,CAAA;CAChB,CAAA;AAED,MAAM,MAAM,mBAAmB,CAAC,CAAC,IAAI;IACnC,EAAE,EAAE,OAAO,CAAA;IACX,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,CAAA;IAC7B,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,CAAA;IACpB,YAAY,EAAE,MAAM,CAAA;CACrB,CAAA;AAED,qBAAa,MAAM;IACjB,GAAG,EAAE,GAAG,CAAA;IACR,KAAK,EAAE,MAAM,EAAE,CAAA;IACf,KAAK,EAAE,YAAY,CAAA;IACnB,UAAU,EAAE,MAAM,CAAA;gBAEN,OAAO,EAAE,aAAa;IAOlC,IAAI;IAIJ,SAAS;IAIT,MAAM,CAAC,aAAa,CAAC,CAAC,SAAS,SAAS,CAAC,YAAY,GAAG,eAAe,CAAC,EACtE,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,EACpB,SAAS,EAAE,OAAO,GAAG,WAAW,GAC/B,mBAAmB,CAAC,CAAC,CAAC;IA0CzB,MAAM,CAAC,cAAc;WAWR,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,UAAO;;;;;;;;;IAyDjF,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;;;;WAqBtC,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,WAA0B;;;;WA2B/D,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;WA0BjD,kBAAkB,CAC7B,KAAK,EAAE,MAAM,EACb,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAClC,IAAI,EAAE,MAAM,EAAE;WA8BH,WAAW,CAAC,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE;;;;;WA2BjE,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;WA0BnD,oBAAoB,CAC/B,KAAK,EAAE,MAAM,EACb,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAClC,IAAI,EAAE,MAAM,EAAE;WA8BH,cAAc,CAAC,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE;;;;;IAyB3E,IAAI,CAAC,YAAY,EAAE,YAAY;;;eAgCuB,WAAW;;;;;;IAOjE,kBAAkB,CAAC,OAAO,EAAE,MAAM;IA+BlC,YAAY;;;;IAqBZ,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE;;;;CAoBpD"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAC,YAAY,EAAE,WAAW,EAAC,MAAM,gBAAgB,CAAA;AAE7D,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,iBAAiB,CAAA;AAEjD,OAAO,EAAC,GAAG,EAAC,MAAM,UAAU,CAAA;AAE5B,OAAO,EACL,OAAO,EAEP,kBAAkB,EAClB,qBAAqB,EAStB,MAAM,cAAc,CAAA;AAErB,MAAM,MAAM,aAAa,GAAG;IAC1B,KAAK,EAAE,YAAY,CAAA;IACnB,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,EAAE,CAAA;CAChB,CAAA;AAED,MAAM,MAAM,mBAAmB,CAAC,CAAC,IAAI;IACnC,EAAE,EAAE,OAAO,CAAA;IACX,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,CAAA;IAC7B,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,CAAA;IACtB,YAAY,EAAE,MAAM,CAAA;CACrB,CAAA;AAED,qBAAa,MAAM;IACjB,GAAG,EAAE,GAAG,CAAA;IACR,KAAK,EAAE,MAAM,EAAE,CAAA;IACf,KAAK,EAAE,YAAY,CAAA;IACnB,UAAU,EAAE,MAAM,CAAA;gBAEN,OAAO,EAAE,aAAa;IAOlC,SAAS;IAIT,MAAM,CAAC,aAAa,CAAC,CAAC,SAAS,kBAAkB,GAAG,qBAAqB,EACvE,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,EACtB,SAAS,EAAE,OAAO,GAAG,WAAW,GAC/B,mBAAmB,CAAC,CAAC,CAAC;IA4CzB,MAAM,CAAC,cAAc;WASR,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,UAAO;;;;;;;;;;;;IAkDjF,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;;;;;;;WAYtC,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,WAA0B;;;;WAuB/D,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;;;;;;;;;;;;;;WAiBjD,kBAAkB,CAC7B,KAAK,EAAE,MAAM,EACb,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAClC,IAAI,EAAE,MAAM,EAAE;;;;;;;;;;;;;;WAuBH,WAAW,CAAC,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE;;;;;;;;;;;;;;;;;;WAcjE,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;;;;;;;;;;;;;;WAiBnD,oBAAoB,CAC/B,KAAK,EAAE,MAAM,EACb,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAClC,IAAI,EAAE,MAAM,EAAE;;;;;;;;;;;;;;WAuBH,cAAc,CAAC,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;;;IAc3E,IAAI,CAAC,YAAY,EAAE,YAAY;;;;;;;;;;;;eAwBuB,WAAW;;;;;;;;;;;;;;;IAOjE,kBAAkB,CAAC,OAAO,EAAE,MAAM;IAyBlC,YAAY;;;;;;;;;;;;;;;;;IAYZ,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE;;;;;;;CASpD"}
package/dist/client.js CHANGED
@@ -1,13 +1,12 @@
1
- import { tryCatch, groupBy, removeUndefined, shuffle, randomId, sortBy, first, last, isDefined, identity, sample, textEncoder, } from "@welshman/lib";
1
+ import { tryCatch, groupBy, removeUndefined, shuffle, randomId, sortBy, first, last, isDefined, sample, textEncoder, } from "@welshman/lib";
2
2
  import { extract } from "@noble/hashes/hkdf.js";
3
3
  import { sha256 } from "@noble/hashes/sha2.js";
4
4
  import { hexToBytes, bytesToHex } from "@noble/hashes/utils.js";
5
- import { prep, makeSecret, makeHttpAuth } from "@welshman/util";
5
+ import { prep, makeSecret } from "@welshman/util";
6
6
  import { Lib } from "@frostr/bifrost";
7
- import { Method } from "./schema.js";
8
7
  import { context, hashEmail, hashPassword } from "./util.js";
9
8
  import { RPC } from "./rpc.js";
10
- import { isEcdhResult, isLoginOptions, isLoginResult, isRecoveryOptions, isRecoveryResult, isRecoverySetupResult, isRegisterResult, isSessionDeleteResult, isSessionListResult, isSignResult, makeChallengeRequest, makeEcdhRequest, makeLoginStart, makeLoginSelect, makeRecoveryStart, makeRecoverySelect, makeRecoverySetup, makeRegisterRequest, makeSessionDelete, makeSessionList, makeSignRequest, } from "./message.js";
9
+ import { PomadeSigner } from "./pomade-signer.js";
11
10
  export class Client {
12
11
  rpc;
13
12
  peers;
@@ -19,17 +18,14 @@ export class Client {
19
18
  this.group = options.group;
20
19
  this.userPubkey = this.group.group_pk.slice(2);
21
20
  }
22
- stop() {
23
- this.rpc.stop();
24
- }
25
21
  getPubkey() {
26
22
  return this.rpc.signer.getPubkey();
27
23
  }
28
24
  static _buildOptions(clientSecret, messages, threshold) {
29
25
  // Extract all items with their metadata
30
- const items = messages.flatMap(m => m?.payload.items?.map(item => ({
26
+ const items = messages.flatMap(m => m.res?.items?.map(item => ({
31
27
  client: item.client,
32
- peer: m.event.pubkey,
28
+ url: m.url,
33
29
  idx: item.idx,
34
30
  total: item.total,
35
31
  threshold: item.threshold,
@@ -49,24 +45,23 @@ export class Client {
49
45
  if (!hasAllIndices)
50
46
  continue;
51
47
  // Sort by idx and map to peers
52
- const peers = sortBy(item => item.idx, clientItems).map(item => item.peer);
48
+ const peers = sortBy(item => item.idx, clientItems).map(item => item.url);
53
49
  options.push([client, peers]);
54
50
  }
55
- const ok = messages.some(m => m?.payload.ok) && options.length > 0;
51
+ const ok = messages.some(m => m.res?.ok) && options.length > 0;
56
52
  return { ok, options, messages, clientSecret };
57
53
  }
58
54
  static _getKnownPeers() {
59
- if (context.signerPubkeys.length === 0) {
60
- console.log("[pomade]: You can configure available signer pubkeys using setSignerPubkeys");
61
- throw new Error("No signer pubkeys available");
55
+ if (context.signerUrls.length === 0) {
56
+ console.log("[pomade]: You can configure available signer URLs using setSignerUrls");
57
+ throw new Error("No signer URLs available");
62
58
  }
63
- return context.signerPubkeys;
59
+ return context.signerUrls;
64
60
  }
65
- // Register
66
61
  static async register(threshold, n, userSecret, recovery = true) {
67
- if (context.signerPubkeys.length < n) {
68
- console.log("[pomade]: You can configure available signer pubkeys using setSignerPubkeys");
69
- throw new Error("Not enough signer pubkeys available");
62
+ if (context.signerUrls.length < n) {
63
+ console.log("[pomade]: You can configure available signer URLs using setSignerUrls");
64
+ throw new Error("Not enough signer URLs available");
70
65
  }
71
66
  if (threshold <= 0) {
72
67
  throw new Error("Threshold must be greater than 0");
@@ -74,27 +69,18 @@ export class Client {
74
69
  const secret = makeSecret();
75
70
  const rpc = RPC.fromSecret(secret);
76
71
  const { group, shares } = Lib.generate_dealer_pkg(threshold, n, [userSecret]);
77
- const remainingSignerPubkeys = shuffle(context.signerPubkeys);
72
+ const remainingSignerUrls = shuffle(context.signerUrls);
78
73
  const peersByIndex = new Map();
79
74
  const messages = await Promise.all(shares.map(async (share, i) => {
80
- while (remainingSignerPubkeys.length > 0) {
81
- const messages = await rpc
82
- .channel(remainingSignerPubkeys.shift())
83
- .send(makeRegisterRequest({ share, group, recovery }))
84
- .receive((message, resolve) => {
85
- if (isRegisterResult(message)) {
86
- if (message.payload.ok) {
87
- peersByIndex.set(i, message.event.pubkey);
88
- }
89
- resolve(message);
90
- }
91
- });
92
- if (peersByIndex.has(i)) {
93
- return messages;
75
+ while (remainingSignerUrls.length > 0) {
76
+ const url = remainingSignerUrls.shift();
77
+ const message = await rpc.post(url, "/register", { share, group, recovery }, context.registerPow);
78
+ if (message.res?.ok) {
79
+ peersByIndex.set(i, url);
94
80
  }
81
+ return message;
95
82
  }
96
83
  }));
97
- rpc.stop();
98
84
  const ok = peersByIndex.size === n;
99
85
  const peers = sortBy(first, peersByIndex).map(last);
100
86
  return {
@@ -107,158 +93,93 @@ export class Client {
107
93
  },
108
94
  };
109
95
  }
110
- // Recovery setup
111
96
  async setupRecovery(email, password) {
112
- const messages = await Promise.all(this.peers.map(async (peer, i) => {
113
- const password_hash = await hashPassword(email, password, peer);
114
- return this.rpc
115
- .channel(peer)
116
- .send(makeRecoverySetup({ email, password_hash }))
117
- .receive((message, resolve) => {
118
- if (isRecoverySetupResult(message)) {
119
- resolve(message);
120
- }
121
- });
97
+ const messages = await Promise.all(this.peers.map(async (url) => {
98
+ const password_hash = await hashPassword(email, password, url);
99
+ return this.rpc.post(url, "/recovery/setup", { email, password_hash });
122
100
  }));
123
- return { ok: messages.every(m => m?.payload.ok), messages };
101
+ return { ok: messages.every(m => m.res?.ok), messages };
124
102
  }
125
- // Challenge
126
103
  static async requestChallenge(email, peers = Client._getKnownPeers()) {
127
104
  const clientSecret = makeSecret();
128
105
  const rpc = RPC.fromSecret(clientSecret);
129
106
  const peersByPrefix = new Map();
130
- const oks = await Promise.all(peers.map(async (peer, i) => {
107
+ const results = await Promise.all(peers.map(async (url) => {
131
108
  let prefix = randomId().slice(-2);
132
109
  while (peersByPrefix.has(prefix)) {
133
110
  prefix = randomId().slice(-2);
134
111
  }
135
- peersByPrefix.set(prefix, peer);
136
- const email_hash = await hashEmail(email, peer);
137
- return rpc.channel(peer).send(makeChallengeRequest({ prefix, email_hash })).ok;
112
+ peersByPrefix.set(prefix, url);
113
+ const email_hash = await hashEmail(email, url);
114
+ return rpc.post(url, "/challenge", { prefix, email_hash });
138
115
  }));
139
- rpc.stop();
140
- return { ok: oks.every(identity), peersByPrefix };
116
+ return { ok: results.every(r => r.res?.ok), peersByPrefix };
141
117
  }
142
- // Login
143
118
  static async loginWithPassword(email, password) {
144
119
  const clientSecret = makeSecret();
145
120
  const rpc = RPC.fromSecret(clientSecret);
146
- const messages = await Promise.all(Client._getKnownPeers().map(async (peer, i) => {
147
- const email_hash = await hashEmail(email, peer);
148
- const password_hash = await hashPassword(email, password, peer);
121
+ const messages = await Promise.all(Client._getKnownPeers().map(async (url) => {
122
+ const email_hash = await hashEmail(email, url);
123
+ const password_hash = await hashPassword(email, password, url);
149
124
  const auth = { email_hash, password_hash };
150
- return rpc
151
- .channel(peer)
152
- .send(makeLoginStart({ auth }))
153
- .receive((message, resolve) => {
154
- if (isLoginOptions(message)) {
155
- resolve(message);
156
- }
157
- });
125
+ return rpc.post(url, "/login/start", { auth });
158
126
  }));
159
- rpc.stop();
160
127
  return this._buildOptions(clientSecret, messages, "total");
161
128
  }
162
129
  static async loginWithChallenge(email, peersByPrefix, otps) {
163
130
  const clientSecret = makeSecret();
164
131
  const rpc = RPC.fromSecret(clientSecret);
165
- const messages = await Promise.all(otps.map(async (otp) => {
166
- const peer = peersByPrefix.get(otp.slice(0, 2));
167
- if (peer) {
168
- const email_hash = await hashEmail(email, peer);
132
+ const messages = removeUndefined(await Promise.all(otps.map(async (otp) => {
133
+ const url = peersByPrefix.get(otp.slice(0, 2));
134
+ if (url) {
135
+ const email_hash = await hashEmail(email, url);
169
136
  const auth = { email_hash, otp };
170
- return rpc
171
- .channel(peer)
172
- .send(makeLoginStart({ auth }))
173
- .receive((message, resolve) => {
174
- if (isLoginOptions(message)) {
175
- resolve(message);
176
- }
177
- });
137
+ return rpc.post(url, "/login/start", { auth });
178
138
  }
179
- }));
180
- rpc.stop();
139
+ })));
181
140
  return this._buildOptions(clientSecret, messages, "total");
182
141
  }
183
142
  static async selectLogin(clientSecret, client, peers) {
184
143
  const rpc = RPC.fromSecret(clientSecret);
185
- const messages = await Promise.all(peers.map((peer, i) => {
186
- return rpc
187
- .channel(peer)
188
- .send(makeLoginSelect({ client }))
189
- .receive((message, resolve) => {
190
- if (isLoginResult(message)) {
191
- resolve(message);
192
- }
193
- });
194
- }));
195
- rpc.stop();
196
- const group = messages.find(m => m?.payload.group)?.payload.group;
197
- const ok = Boolean(group && messages.every(m => m?.payload.ok));
144
+ const messages = await Promise.all(peers.map(url => rpc.post(url, "/login/select", { client })));
145
+ const group = messages.find(m => m.res?.group)?.res?.group;
146
+ const ok = Boolean(group && messages.every(m => m.res?.ok));
198
147
  const clientOptions = ok ? { group, peers, secret: clientSecret } : undefined;
199
148
  return { ok, messages, clientOptions };
200
149
  }
201
- // Recovery
202
150
  static async recoverWithPassword(email, password) {
203
151
  const clientSecret = makeSecret();
204
152
  const rpc = RPC.fromSecret(clientSecret);
205
- const messages = await Promise.all(Client._getKnownPeers().map(async (peer, i) => {
206
- const email_hash = await hashEmail(email, peer);
207
- const password_hash = await hashPassword(email, password, peer);
153
+ const messages = await Promise.all(Client._getKnownPeers().map(async (url) => {
154
+ const email_hash = await hashEmail(email, url);
155
+ const password_hash = await hashPassword(email, password, url);
208
156
  const auth = { email_hash, password_hash };
209
- return rpc
210
- .channel(peer)
211
- .send(makeRecoveryStart({ auth }))
212
- .receive((message, resolve) => {
213
- if (isRecoveryOptions(message)) {
214
- resolve(message);
215
- }
216
- });
157
+ return rpc.post(url, "/recovery/start", { auth });
217
158
  }));
218
- rpc.stop();
219
159
  return this._buildOptions(clientSecret, messages, "threshold");
220
160
  }
221
161
  static async recoverWithChallenge(email, peersByPrefix, otps) {
222
162
  const clientSecret = makeSecret();
223
163
  const rpc = RPC.fromSecret(clientSecret);
224
- const messages = await Promise.all(otps.map(async (otp) => {
225
- const peer = peersByPrefix.get(otp.slice(0, 2));
226
- if (peer) {
227
- const email_hash = await hashEmail(email, peer);
164
+ const messages = removeUndefined(await Promise.all(otps.map(async (otp) => {
165
+ const url = peersByPrefix.get(otp.slice(0, 2));
166
+ if (url) {
167
+ const email_hash = await hashEmail(email, url);
228
168
  const auth = { email_hash, otp };
229
- return rpc
230
- .channel(peer)
231
- .send(makeRecoveryStart({ auth }))
232
- .receive((message, resolve) => {
233
- if (isRecoveryOptions(message)) {
234
- resolve(message);
235
- }
236
- });
169
+ return rpc.post(url, "/recovery/start", { auth });
237
170
  }
238
- }));
239
- rpc.stop();
171
+ })));
240
172
  return this._buildOptions(clientSecret, messages, "threshold");
241
173
  }
242
174
  static async selectRecovery(clientSecret, client, peers) {
243
175
  const rpc = RPC.fromSecret(clientSecret);
244
- const messages = await Promise.all(peers.map(peer => {
245
- return rpc
246
- .channel(peer)
247
- .send(makeRecoverySelect({ client }))
248
- .receive((message, resolve) => {
249
- if (isRecoveryResult(message)) {
250
- resolve(message);
251
- }
252
- });
253
- }));
254
- rpc.stop();
255
- const group = messages.find(m => m?.payload.group)?.payload.group;
256
- const shares = removeUndefined(messages.map(m => m?.payload.share));
176
+ const messages = await Promise.all(peers.map(url => rpc.post(url, "/recovery/select", { client })));
177
+ const group = messages.find(m => m.res?.group)?.res?.group;
178
+ const shares = removeUndefined(messages.map(m => m.res?.share));
257
179
  const userSecret = tryCatch(() => Lib.recover_secret_key(group, shares));
258
180
  return { ok: Boolean(userSecret), messages, userSecret };
259
181
  }
260
182
  async sign(stampedEvent) {
261
- // TODO: optimize this so that all signers are asked, but only the fastest results get used
262
183
  const { threshold, commits } = this.group;
263
184
  const event = prep(stampedEvent, this.userPubkey);
264
185
  const members = sample(threshold, commits).map(c => c.idx);
@@ -267,19 +188,12 @@ export class Client {
267
188
  throw new Error("Failed to create signing template");
268
189
  const request = Lib.create_session_pkg(this.group, template);
269
190
  const messages = await Promise.all(members.map(idx => {
270
- const peer = this.peers[idx - 1];
271
- return this.rpc
272
- .channel(peer)
273
- .send(makeSignRequest({ request }))
274
- .receive((message, resolve) => {
275
- if (isSignResult(message)) {
276
- resolve(message);
277
- }
278
- });
191
+ const url = this.peers[idx - 1];
192
+ return this.rpc.post(url, "/sign", { request });
279
193
  }));
280
- if (messages.every(m => m?.payload.ok)) {
194
+ if (messages.every(m => m.res?.ok)) {
281
195
  const ctx = Lib.get_session_ctx(this.group, request);
282
- const pkgs = messages.map(m => m.payload.result);
196
+ const pkgs = messages.map(m => m.res.result);
283
197
  const sig = Lib.combine_signature_pkgs(ctx, pkgs)[0]?.[2];
284
198
  if (sig) {
285
199
  return { ok: true, messages, event: { ...event, sig } };
@@ -288,55 +202,27 @@ export class Client {
288
202
  return { ok: false, messages };
289
203
  }
290
204
  async getConversationKey(ecdh_pk) {
291
- // TODO: optimize this so that all signers are asked, but only the fastest results get used
292
205
  const { threshold, commits } = this.group;
293
206
  const members = sample(threshold, commits).map(c => c.idx);
294
207
  const results = await Promise.all(members.map(idx => {
295
- const peer = this.peers[idx - 1];
296
- const channel = this.rpc.channel(peer);
297
- return channel
298
- .send(makeEcdhRequest({ idx, members, ecdh_pk }))
299
- .receive((message, resolve) => {
300
- if (isEcdhResult(message)) {
301
- resolve(message.payload.result);
302
- }
303
- });
208
+ const url = this.peers[idx - 1];
209
+ return this.rpc
210
+ .post(url, "/ecdh", { idx, members, ecdh_pk })
211
+ .then(r => r.res?.result);
304
212
  }));
305
213
  if (results.every(isDefined)) {
306
214
  return bytesToHex(extract(sha256, hexToBytes(Lib.combine_ecdh_pkgs(results).slice(2)), textEncoder.encode("nip44-v2")));
307
215
  }
308
216
  }
309
217
  async listSessions() {
310
- const messages = await Promise.all(Client._getKnownPeers().map(async (peer, i) => {
311
- const { event: auth } = await this.sign(await makeHttpAuth(peer, Method.SessionList));
312
- if (auth) {
313
- return this.rpc
314
- .channel(peer)
315
- .send(makeSessionList({ auth }))
316
- .receive((message, resolve) => {
317
- if (isSessionListResult(message)) {
318
- resolve(message);
319
- }
320
- });
321
- }
322
- }));
323
- return { ok: messages.every(m => m?.payload.ok), messages };
218
+ const userRpc = new RPC(new PomadeSigner(this));
219
+ const messages = await Promise.all(Client._getKnownPeers().map(url => userRpc.post(url, "/session/list", {})));
220
+ return { ok: messages.every(m => m.res?.ok), messages };
324
221
  }
325
222
  async deleteSession(client, peers) {
326
- const messages = await Promise.all(peers.map(async (peer, i) => {
327
- const { event: auth } = await this.sign(await makeHttpAuth(peer, Method.SessionDelete));
328
- if (auth) {
329
- return this.rpc
330
- .channel(peer)
331
- .send(makeSessionDelete({ client, auth }))
332
- .receive((message, resolve) => {
333
- if (isSessionDeleteResult(message)) {
334
- resolve(message);
335
- }
336
- });
337
- }
338
- }));
339
- return { ok: messages.every(m => m?.payload.ok), messages };
223
+ const userRpc = new RPC(new PomadeSigner(this));
224
+ const messages = await Promise.all(peers.map(url => userRpc.post(url, "/session/delete", { client })));
225
+ return { ok: messages.every(m => m.res?.ok), messages };
340
226
  }
341
227
  }
342
228
  //# sourceMappingURL=client.js.map