nostr-double-ratchet 0.0.6 → 0.0.8
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/README.md +2 -1
- package/dist/Channel.d.ts +4 -0
- package/dist/Channel.d.ts.map +1 -1
- package/dist/{InviteLink.d.ts → Invite.d.ts} +10 -7
- package/dist/Invite.d.ts.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/nostr-double-ratchet.es.js +1227 -1145
- package/dist/nostr-double-ratchet.umd.js +1 -1
- package/dist/types.d.ts +7 -7
- package/dist/types.d.ts.map +1 -1
- package/dist/utils.d.ts.map +1 -1
- package/package.json +2 -1
- package/src/Channel.ts +73 -33
- package/src/{InviteLink.ts → Invite.ts} +73 -13
- package/src/index.ts +1 -1
- package/src/types.ts +9 -9
- package/src/utils.ts +12 -0
- package/dist/InviteLink.d.ts.map +0 -1
package/src/Channel.ts
CHANGED
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
Unsubscribe,
|
|
7
7
|
NostrSubscribe,
|
|
8
8
|
MessageCallback,
|
|
9
|
-
|
|
9
|
+
MESSAGE_EVENT_KIND,
|
|
10
10
|
} from "./types";
|
|
11
11
|
import { kdf, skippedMessageIndexKey } from "./utils";
|
|
12
12
|
|
|
@@ -62,6 +62,7 @@ export class Channel {
|
|
|
62
62
|
receivingChainMessageNumber: 0,
|
|
63
63
|
previousSendingChainMessageCount: 0,
|
|
64
64
|
skippedMessageKeys: {},
|
|
65
|
+
skippedHeaderKeys: {},
|
|
65
66
|
};
|
|
66
67
|
const channel = new Channel(nostrSubscribe, state);
|
|
67
68
|
if (name) channel.name = name;
|
|
@@ -86,7 +87,7 @@ export class Channel {
|
|
|
86
87
|
|
|
87
88
|
const nostrEvent = finalizeEvent({
|
|
88
89
|
content: encryptedData,
|
|
89
|
-
kind:
|
|
90
|
+
kind: MESSAGE_EVENT_KIND,
|
|
90
91
|
tags: [["header", encryptedHeader]],
|
|
91
92
|
created_at: Math.floor(Date.now() / 1000)
|
|
92
93
|
}, this.state.ourCurrentNostrKey.privateKey);
|
|
@@ -106,6 +107,14 @@ export class Channel {
|
|
|
106
107
|
return () => this.internalSubscriptions.delete(id)
|
|
107
108
|
}
|
|
108
109
|
|
|
110
|
+
/**
|
|
111
|
+
* Stop listening to incoming messages
|
|
112
|
+
*/
|
|
113
|
+
close() {
|
|
114
|
+
this.nostrUnsubscribe?.();
|
|
115
|
+
this.nostrNextUnsubscribe?.();
|
|
116
|
+
}
|
|
117
|
+
|
|
109
118
|
// 2. RATCHET FUNCTIONS
|
|
110
119
|
private ratchetEncrypt(plaintext: string): [Header, string] {
|
|
111
120
|
const [newSendingChainKey, messageKey] = kdf(this.state.sendingChainKey!, new Uint8Array([1]), 2);
|
|
@@ -176,6 +185,18 @@ export class Channel {
|
|
|
176
185
|
this.state.receivingChainKey = newReceivingChainKey;
|
|
177
186
|
const key = skippedMessageIndexKey(nostrSender, this.state.receivingChainMessageNumber);
|
|
178
187
|
this.state.skippedMessageKeys[key] = messageKey;
|
|
188
|
+
|
|
189
|
+
if (!this.state.skippedHeaderKeys[nostrSender]) {
|
|
190
|
+
const secrets: Uint8Array[] = [];
|
|
191
|
+
if (this.state.ourCurrentNostrKey) {
|
|
192
|
+
const currentSecret = nip44.getConversationKey(this.state.ourCurrentNostrKey.privateKey, nostrSender);
|
|
193
|
+
secrets.push(currentSecret);
|
|
194
|
+
}
|
|
195
|
+
const nextSecret = nip44.getConversationKey(this.state.ourNextNostrKey.privateKey, nostrSender);
|
|
196
|
+
secrets.push(nextSecret);
|
|
197
|
+
this.state.skippedHeaderKeys[nostrSender] = secrets;
|
|
198
|
+
}
|
|
199
|
+
|
|
179
200
|
this.state.receivingChainMessageNumber++;
|
|
180
201
|
}
|
|
181
202
|
}
|
|
@@ -185,19 +206,29 @@ export class Channel {
|
|
|
185
206
|
if (key in this.state.skippedMessageKeys) {
|
|
186
207
|
const mk = this.state.skippedMessageKeys[key];
|
|
187
208
|
delete this.state.skippedMessageKeys[key];
|
|
209
|
+
|
|
210
|
+
// Check if we have any remaining skipped messages from this sender
|
|
211
|
+
const hasMoreSkippedMessages = Object.keys(this.state.skippedMessageKeys).some(k => k.startsWith(`${nostrSender}:`));
|
|
212
|
+
if (!hasMoreSkippedMessages) {
|
|
213
|
+
// Clean up header keys and unsubscribe as no more skipped messages from this sender
|
|
214
|
+
delete this.state.skippedHeaderKeys[nostrSender];
|
|
215
|
+
this.nostrUnsubscribe?.();
|
|
216
|
+
this.nostrUnsubscribe = undefined;
|
|
217
|
+
}
|
|
218
|
+
|
|
188
219
|
return nip44.decrypt(ciphertext, mk);
|
|
189
220
|
}
|
|
190
221
|
return null;
|
|
191
222
|
}
|
|
192
223
|
|
|
193
224
|
// 4. NOSTR EVENT HANDLING
|
|
194
|
-
private decryptHeader(event: any): [Header, boolean] {
|
|
225
|
+
private decryptHeader(event: any): [Header, boolean, boolean] {
|
|
195
226
|
const encryptedHeader = event.tags[0][1];
|
|
196
227
|
if (this.state.ourCurrentNostrKey) {
|
|
197
228
|
const currentSecret = nip44.getConversationKey(this.state.ourCurrentNostrKey.privateKey, event.pubkey);
|
|
198
229
|
try {
|
|
199
230
|
const header = JSON.parse(nip44.decrypt(encryptedHeader, currentSecret)) as Header;
|
|
200
|
-
return [header, false];
|
|
231
|
+
return [header, false, false];
|
|
201
232
|
} catch (error) {
|
|
202
233
|
// Decryption with currentSecret failed, try with nextSecret
|
|
203
234
|
}
|
|
@@ -206,32 +237,49 @@ export class Channel {
|
|
|
206
237
|
const nextSecret = nip44.getConversationKey(this.state.ourNextNostrKey.privateKey, event.pubkey);
|
|
207
238
|
try {
|
|
208
239
|
const header = JSON.parse(nip44.decrypt(encryptedHeader, nextSecret)) as Header;
|
|
209
|
-
return [header, true];
|
|
240
|
+
return [header, true, false];
|
|
210
241
|
} catch (error) {
|
|
211
242
|
// Decryption with nextSecret also failed
|
|
212
243
|
}
|
|
213
244
|
|
|
214
|
-
|
|
245
|
+
const keys = this.state.skippedHeaderKeys[event.pubkey];
|
|
246
|
+
if (keys) {
|
|
247
|
+
for (const key of keys) {
|
|
248
|
+
try {
|
|
249
|
+
const header = JSON.parse(nip44.decrypt(encryptedHeader, key)) as Header;
|
|
250
|
+
return [header, false, true];
|
|
251
|
+
} catch (error) {
|
|
252
|
+
// Decryption failed, try next secret
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
215
256
|
|
|
216
|
-
|
|
257
|
+
throw new Error("Failed to decrypt header with current and skipped header keys");
|
|
217
258
|
}
|
|
218
259
|
|
|
219
260
|
private handleNostrEvent(e: any) {
|
|
220
|
-
const [header, shouldRatchet] = this.decryptHeader(e);
|
|
261
|
+
const [header, shouldRatchet, isSkipped] = this.decryptHeader(e);
|
|
221
262
|
|
|
222
|
-
if (
|
|
223
|
-
this.state.theirNostrPublicKey
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
263
|
+
if (!isSkipped) {
|
|
264
|
+
if (this.state.theirNostrPublicKey !== header.nextPublicKey) {
|
|
265
|
+
this.state.theirNostrPublicKey = header.nextPublicKey;
|
|
266
|
+
this.nostrUnsubscribe?.(); // should we keep this open for a while? maybe as long as we have skipped messages?
|
|
267
|
+
this.nostrUnsubscribe = this.nostrNextUnsubscribe;
|
|
268
|
+
this.nostrNextUnsubscribe = this.nostrSubscribe(
|
|
269
|
+
{authors: [this.state.theirNostrPublicKey], kinds: [MESSAGE_EVENT_KIND]},
|
|
270
|
+
(e) => this.handleNostrEvent(e)
|
|
271
|
+
);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
if (shouldRatchet) {
|
|
275
|
+
this.skipMessageKeys(header.previousChainLength, e.pubkey);
|
|
276
|
+
this.ratchetStep(header.nextPublicKey);
|
|
277
|
+
}
|
|
278
|
+
} else {
|
|
279
|
+
const key = skippedMessageIndexKey(e.pubkey, header.number);
|
|
280
|
+
if (!(key in this.state.skippedMessageKeys)) {
|
|
281
|
+
return // maybe we already processed this message
|
|
282
|
+
}
|
|
235
283
|
}
|
|
236
284
|
|
|
237
285
|
const data = this.ratchetDecrypt(header, e.content, e.pubkey);
|
|
@@ -242,24 +290,16 @@ export class Channel {
|
|
|
242
290
|
private subscribeToNostrEvents() {
|
|
243
291
|
if (this.nostrNextUnsubscribe) return;
|
|
244
292
|
this.nostrNextUnsubscribe = this.nostrSubscribe(
|
|
245
|
-
{authors: [this.state.theirNostrPublicKey], kinds: [
|
|
293
|
+
{authors: [this.state.theirNostrPublicKey], kinds: [MESSAGE_EVENT_KIND]},
|
|
246
294
|
(e) => this.handleNostrEvent(e)
|
|
247
295
|
);
|
|
248
|
-
|
|
249
|
-
// Subscribe to all public keys from skipped messages
|
|
250
|
-
const uniquePublicKeys = new Set<string>();
|
|
251
|
-
for (const key of Object.keys(this.state.skippedMessageKeys)) {
|
|
252
|
-
const [pubkey] = key.split(':');
|
|
253
|
-
if (pubkey !== this.state.theirNostrPublicKey) {
|
|
254
|
-
uniquePublicKeys.add(pubkey);
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
296
|
|
|
258
|
-
|
|
297
|
+
const skippedSenders = Object.keys(this.state.skippedHeaderKeys);
|
|
298
|
+
if (skippedSenders.length > 0) {
|
|
259
299
|
// do we want this unsubscribed on rotation or should we keep it open
|
|
260
300
|
// in case more skipped messages are found by relays or peers?
|
|
261
301
|
this.nostrUnsubscribe = this.nostrSubscribe(
|
|
262
|
-
{authors:
|
|
302
|
+
{authors: skippedSenders, kinds: [MESSAGE_EVENT_KIND]},
|
|
263
303
|
(e) => this.handleNostrEvent(e)
|
|
264
304
|
);
|
|
265
305
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { generateSecretKey, getPublicKey, nip44, finalizeEvent, VerifiedEvent, nip19 } from "nostr-tools";
|
|
2
|
-
import { NostrSubscribe, Unsubscribe } from "./types";
|
|
1
|
+
import { generateSecretKey, getPublicKey, nip44, finalizeEvent, VerifiedEvent, nip19, UnsignedEvent, verifyEvent } from "nostr-tools";
|
|
2
|
+
import { INVITE_EVENT_KIND, NostrSubscribe, Unsubscribe } from "./types";
|
|
3
3
|
import { getConversationKey } from "nostr-tools/nip44";
|
|
4
4
|
import { Channel } from "./Channel";
|
|
5
|
-
import {
|
|
5
|
+
import { MESSAGE_EVENT_KIND } from "./types";
|
|
6
6
|
import { EncryptFunction, DecryptFunction } from "./types";
|
|
7
7
|
import { hexToBytes, bytesToHex } from "@noble/hashes/utils";
|
|
8
8
|
|
|
@@ -17,7 +17,7 @@ import { hexToBytes, bytesToHex } from "@noble/hashes/utils";
|
|
|
17
17
|
*
|
|
18
18
|
* Also make sure to keep the session key safe.
|
|
19
19
|
*/
|
|
20
|
-
export class
|
|
20
|
+
export class Invite {
|
|
21
21
|
constructor(
|
|
22
22
|
public inviterSessionPublicKey: string,
|
|
23
23
|
public linkSecret: string,
|
|
@@ -28,11 +28,11 @@ export class InviteLink {
|
|
|
28
28
|
public usedBy: string[] = [],
|
|
29
29
|
) {}
|
|
30
30
|
|
|
31
|
-
static createNew(inviter: string, label?: string, maxUses?: number):
|
|
31
|
+
static createNew(inviter: string, label?: string, maxUses?: number): Invite {
|
|
32
32
|
const inviterSessionPrivateKey = generateSecretKey();
|
|
33
33
|
const inviterSessionPublicKey = getPublicKey(inviterSessionPrivateKey);
|
|
34
34
|
const linkSecret = bytesToHex(generateSecretKey());
|
|
35
|
-
return new
|
|
35
|
+
return new Invite(
|
|
36
36
|
inviterSessionPublicKey,
|
|
37
37
|
linkSecret,
|
|
38
38
|
inviter,
|
|
@@ -42,7 +42,7 @@ export class InviteLink {
|
|
|
42
42
|
);
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
static fromUrl(url: string):
|
|
45
|
+
static fromUrl(url: string): Invite {
|
|
46
46
|
const parsedUrl = new URL(url);
|
|
47
47
|
const rawHash = parsedUrl.hash.slice(1);
|
|
48
48
|
if (!rawHash) {
|
|
@@ -72,16 +72,16 @@ export class InviteLink {
|
|
|
72
72
|
throw new Error("Decoded session key is not a string");
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
return new
|
|
75
|
+
return new Invite(
|
|
76
76
|
decodedSessionKey.data,
|
|
77
77
|
linkSecret,
|
|
78
78
|
decodedInviter.data
|
|
79
79
|
);
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
static deserialize(json: string):
|
|
82
|
+
static deserialize(json: string): Invite {
|
|
83
83
|
const data = JSON.parse(json);
|
|
84
|
-
return new
|
|
84
|
+
return new Invite(
|
|
85
85
|
data.inviterSessionPublicKey,
|
|
86
86
|
data.linkSecret,
|
|
87
87
|
data.inviter,
|
|
@@ -92,6 +92,56 @@ export class InviteLink {
|
|
|
92
92
|
);
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
+
static fromEvent(event: VerifiedEvent): Invite {
|
|
96
|
+
if (!event.sig) {
|
|
97
|
+
throw new Error("Event is not signed");
|
|
98
|
+
}
|
|
99
|
+
if (!verifyEvent(event)) {
|
|
100
|
+
throw new Error("Event signature is invalid");
|
|
101
|
+
}
|
|
102
|
+
const { tags } = event;
|
|
103
|
+
const inviterSessionPublicKey = tags.find(([key]) => key === 'sessionKey')?.[1];
|
|
104
|
+
const linkSecret = tags.find(([key]) => key === 'linkSecret')?.[1];
|
|
105
|
+
const inviter = event.pubkey;
|
|
106
|
+
|
|
107
|
+
if (!inviterSessionPublicKey || !linkSecret) {
|
|
108
|
+
throw new Error("Invalid invite event: missing session key or link secret");
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return new Invite(
|
|
112
|
+
inviterSessionPublicKey,
|
|
113
|
+
linkSecret,
|
|
114
|
+
inviter
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
static fromUser(user: string, subscribe: NostrSubscribe): Promise<Invite | undefined> {
|
|
119
|
+
const filter = {
|
|
120
|
+
kinds: [INVITE_EVENT_KIND],
|
|
121
|
+
pubkey: user,
|
|
122
|
+
limit: 1,
|
|
123
|
+
"#d": ["nostr-double-ratchet/invite"],
|
|
124
|
+
};
|
|
125
|
+
return new Promise((resolve) => {
|
|
126
|
+
const unsub = subscribe(filter, (event) => {
|
|
127
|
+
try {
|
|
128
|
+
const inviteLink = Invite.fromEvent(event);
|
|
129
|
+
unsub();
|
|
130
|
+
resolve(inviteLink);
|
|
131
|
+
} catch (error) {
|
|
132
|
+
unsub();
|
|
133
|
+
resolve(undefined);
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
// Set timeout to unsubscribe and return undefined after 10 seconds
|
|
138
|
+
setTimeout(() => {
|
|
139
|
+
unsub();
|
|
140
|
+
resolve(undefined);
|
|
141
|
+
}, 10000);
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
95
145
|
serialize(): string {
|
|
96
146
|
return JSON.stringify({
|
|
97
147
|
inviterSessionPublicKey: this.inviterSessionPublicKey,
|
|
@@ -116,6 +166,16 @@ export class InviteLink {
|
|
|
116
166
|
return url.toString();
|
|
117
167
|
}
|
|
118
168
|
|
|
169
|
+
getEvent(): UnsignedEvent {
|
|
170
|
+
return {
|
|
171
|
+
kind: INVITE_EVENT_KIND,
|
|
172
|
+
pubkey: this.inviter,
|
|
173
|
+
content: "",
|
|
174
|
+
created_at: Math.floor(Date.now() / 1000),
|
|
175
|
+
tags: [['sessionKey', this.inviterSessionPublicKey], ['linkSecret', this.linkSecret], ['d', 'nostr-double-ratchet/invite']],
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
|
|
119
179
|
/**
|
|
120
180
|
* Accepts the invite and creates a new channel with the inviter.
|
|
121
181
|
*
|
|
@@ -131,7 +191,7 @@ export class InviteLink {
|
|
|
131
191
|
* Note: You need to publish the returned event on Nostr using NDK or another Nostr system of your choice,
|
|
132
192
|
* so the inviter can create the channel on their side.
|
|
133
193
|
*/
|
|
134
|
-
async
|
|
194
|
+
async accept(
|
|
135
195
|
nostrSubscribe: NostrSubscribe,
|
|
136
196
|
inviteePublicKey: string,
|
|
137
197
|
inviteeSecretKey: Uint8Array | EncryptFunction,
|
|
@@ -159,7 +219,7 @@ export class InviteLink {
|
|
|
159
219
|
};
|
|
160
220
|
|
|
161
221
|
const envelope = {
|
|
162
|
-
kind:
|
|
222
|
+
kind: MESSAGE_EVENT_KIND,
|
|
163
223
|
pubkey: randomSenderPublicKey,
|
|
164
224
|
content: nip44.encrypt(JSON.stringify(innerEvent), getConversationKey(randomSenderKey, this.inviterSessionPublicKey)),
|
|
165
225
|
created_at: Math.floor(Date.now() / 1000),
|
|
@@ -175,7 +235,7 @@ export class InviteLink {
|
|
|
175
235
|
}
|
|
176
236
|
|
|
177
237
|
const filter = {
|
|
178
|
-
kinds: [
|
|
238
|
+
kinds: [MESSAGE_EVENT_KIND],
|
|
179
239
|
'#p': [this.inviterSessionPublicKey],
|
|
180
240
|
};
|
|
181
241
|
|
package/src/index.ts
CHANGED
package/src/types.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { VerifiedEvent } from "nostr-tools";
|
|
1
|
+
import { Filter, UnsignedEvent, VerifiedEvent } from "nostr-tools";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* An event that has been verified to be from the Nostr network.
|
|
@@ -17,11 +17,6 @@ export type Header = {
|
|
|
17
17
|
time: number;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
export type NostrFilter = {
|
|
21
|
-
authors?: string[];
|
|
22
|
-
kinds?: number[];
|
|
23
|
-
}
|
|
24
|
-
|
|
25
20
|
/**
|
|
26
21
|
* A keypair used for encryption and decryption.
|
|
27
22
|
*/
|
|
@@ -63,6 +58,9 @@ export interface ChannelState {
|
|
|
63
58
|
|
|
64
59
|
/** Cache of message keys for handling out-of-order messages */
|
|
65
60
|
skippedMessageKeys: Record<string, Uint8Array>;
|
|
61
|
+
|
|
62
|
+
/** Cache of header keys for handling out-of-order messages */
|
|
63
|
+
skippedHeaderKeys: Record<string, Uint8Array[]>;
|
|
66
64
|
}
|
|
67
65
|
|
|
68
66
|
/**
|
|
@@ -73,7 +71,7 @@ export type Unsubscribe = () => void;
|
|
|
73
71
|
/**
|
|
74
72
|
* Function that subscribes to Nostr events matching a filter and calls onEvent for each event.
|
|
75
73
|
*/
|
|
76
|
-
export type NostrSubscribe = (filter:
|
|
74
|
+
export type NostrSubscribe = (filter: Filter, onEvent: (e: VerifiedEvent) => void) => Unsubscribe;
|
|
77
75
|
|
|
78
76
|
/**
|
|
79
77
|
* Callback function for handling decrypted messages
|
|
@@ -81,7 +79,8 @@ export type NostrSubscribe = (filter: NostrFilter, onEvent: (e: VerifiedEvent) =
|
|
|
81
79
|
*/
|
|
82
80
|
export type MessageCallback = (message: Message) => void;
|
|
83
81
|
|
|
84
|
-
export const
|
|
82
|
+
export const MESSAGE_EVENT_KIND = 4;
|
|
83
|
+
export const INVITE_EVENT_KIND = 30078;
|
|
85
84
|
export const MAX_SKIP = 100;
|
|
86
85
|
|
|
87
86
|
export type NostrEvent = {
|
|
@@ -100,4 +99,5 @@ export enum Sender {
|
|
|
100
99
|
}
|
|
101
100
|
|
|
102
101
|
export type EncryptFunction = (plaintext: string, pubkey: string) => Promise<string>;
|
|
103
|
-
export type DecryptFunction = (ciphertext: string, pubkey: string) => Promise<string>;
|
|
102
|
+
export type DecryptFunction = (ciphertext: string, pubkey: string) => Promise<string>;
|
|
103
|
+
export type NostrPublish = (event: UnsignedEvent) => Promise<VerifiedEvent>;
|
package/src/utils.ts
CHANGED
|
@@ -27,6 +27,12 @@ export function serializeChannelState(state: ChannelState): string {
|
|
|
27
27
|
bytesToHex(value),
|
|
28
28
|
])
|
|
29
29
|
),
|
|
30
|
+
skippedHeaderKeys: Object.fromEntries(
|
|
31
|
+
Object.entries(state.skippedHeaderKeys).map(([key, value]) => [
|
|
32
|
+
key,
|
|
33
|
+
value.map(bytes => bytesToHex(bytes))
|
|
34
|
+
])
|
|
35
|
+
),
|
|
30
36
|
});
|
|
31
37
|
}
|
|
32
38
|
|
|
@@ -54,6 +60,12 @@ export function deserializeChannelState(data: string): ChannelState {
|
|
|
54
60
|
hexToBytes(value as string),
|
|
55
61
|
])
|
|
56
62
|
),
|
|
63
|
+
skippedHeaderKeys: Object.fromEntries(
|
|
64
|
+
Object.entries(state.skippedHeaderKeys || {}).map(([key, value]) => [
|
|
65
|
+
key,
|
|
66
|
+
(value as string[]).map(hex => hexToBytes(hex))
|
|
67
|
+
])
|
|
68
|
+
),
|
|
57
69
|
};
|
|
58
70
|
}
|
|
59
71
|
|
package/dist/InviteLink.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"InviteLink.d.ts","sourceRoot":"","sources":["../src/InviteLink.ts"],"names":[],"mappings":"AAAA,OAAO,EAAyD,aAAa,EAAS,MAAM,aAAa,CAAC;AAC1G,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAEtD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAG3D;;;;;;;;;;GAUG;AACH,qBAAa,UAAU;IAER,uBAAuB,EAAE,MAAM;IAC/B,UAAU,EAAE,MAAM;IAClB,OAAO,EAAE,MAAM;IACf,wBAAwB,CAAC,EAAE,UAAU;IACrC,KAAK,CAAC,EAAE,MAAM;IACd,OAAO,CAAC,EAAE,MAAM;IAChB,MAAM,EAAE,MAAM,EAAE;gBANhB,uBAAuB,EAAE,MAAM,EAC/B,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,EACf,wBAAwB,CAAC,EAAE,UAAU,YAAA,EACrC,KAAK,CAAC,EAAE,MAAM,YAAA,EACd,OAAO,CAAC,EAAE,MAAM,YAAA,EAChB,MAAM,GAAE,MAAM,EAAO;IAGhC,MAAM,CAAC,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,UAAU;IAc/E,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU;IAqCvC,MAAM,CAAC,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU;IAa5C,SAAS,IAAI,MAAM;IAYnB,MAAM,CAAC,IAAI,SAAoB;IAY/B;;;;;;;;;;;;;;OAcG;IACG,YAAY,CACd,cAAc,EAAE,cAAc,EAC9B,gBAAgB,EAAE,MAAM,EACxB,gBAAgB,EAAE,UAAU,GAAG,eAAe,GAC/C,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,KAAK,EAAE,aAAa,CAAA;KAAE,CAAC;IAkCtD,MAAM,CAAC,gBAAgB,EAAE,UAAU,GAAG,eAAe,EAAE,cAAc,EAAE,cAAc,EAAE,SAAS,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,MAAM,KAAK,IAAI,GAAG,WAAW;CAoChK"}
|