add encryption/decryotion and other features

This commit is contained in:
Sasha 2023-11-23 16:43:30 +01:00
parent cd8085f6e5
commit 714c64387e
No known key found for this signature in database
13 changed files with 556 additions and 112 deletions

View File

@ -9,7 +9,10 @@
"version": "0.1.0",
"dependencies": {
"@waku/interfaces": "^0.0.20",
"@waku/message-encryption": "^0.0.23",
"@waku/sdk": "^0.0.21",
"@waku/utils": "^0.0.13",
"ethereum-cryptography": "^2.1.2",
"next": "14.0.2",
"react": "^18",
"react-dom": "^18",
@ -921,6 +924,61 @@
"integrity": "sha512-6i/8UoL0P5y4leBIGzvkZdS85RDMG9y1ihZzmTZQ5LdHUYmZ7pKFoj8X0236s3lusPs1Fa5HTQUpwI+UfTcmeA==",
"dev": true
},
"node_modules/@scure/base": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.3.tgz",
"integrity": "sha512-/+SgoRjLq7Xlf0CWuLHq2LUZeL/w65kfzAPG5NH9pcmBhs+nunQTn4gvdwgMTIXnt9b2C/1SeL2XiysZEyIC9Q==",
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@scure/bip32": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.3.1.tgz",
"integrity": "sha512-osvveYtyzdEVbt3OfwwXFr4P2iVBL5u1Q3q4ONBfDY/UpOuXmOlbgwc1xECEboY8wIays8Yt6onaWMUdUbfl0A==",
"dependencies": {
"@noble/curves": "~1.1.0",
"@noble/hashes": "~1.3.1",
"@scure/base": "~1.1.0"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@scure/bip32/node_modules/@noble/curves": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.1.0.tgz",
"integrity": "sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA==",
"dependencies": {
"@noble/hashes": "1.3.1"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@scure/bip32/node_modules/@noble/hashes": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz",
"integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==",
"engines": {
"node": ">= 16"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@scure/bip39": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.2.1.tgz",
"integrity": "sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg==",
"dependencies": {
"@noble/hashes": "~1.3.0",
"@scure/base": "~1.1.0"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@swc/helpers": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.2.tgz",
@ -1228,6 +1286,23 @@
"node": ">=18"
}
},
"node_modules/@waku/message-encryption": {
"version": "0.0.23",
"resolved": "https://registry.npmjs.org/@waku/message-encryption/-/message-encryption-0.0.23.tgz",
"integrity": "sha512-i/qMAjO8EVCMlA1BbuGT+uHxzdydYQVXM9mThgH+kTTPTW5gVAMdzFaGmbiHWT6kn3yAsWq5CBB7M3+1zqb8vQ==",
"dependencies": {
"@noble/secp256k1": "^1.7.1",
"@waku/core": "0.0.25",
"@waku/interfaces": "0.0.20",
"@waku/proto": "0.0.5",
"@waku/utils": "0.0.13",
"debug": "^4.3.4",
"js-sha3": "^0.9.2"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@waku/peer-exchange": {
"version": "0.0.18",
"resolved": "https://registry.npmjs.org/@waku/peer-exchange/-/peer-exchange-0.0.18.tgz",
@ -2650,6 +2725,39 @@
"node": ">=0.10.0"
}
},
"node_modules/ethereum-cryptography": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.1.2.tgz",
"integrity": "sha512-Z5Ba0T0ImZ8fqXrJbpHcbpAvIswRte2wGNR/KePnu8GbbvgJ47lMxT/ZZPG6i9Jaht4azPDop4HaM00J0J59ug==",
"dependencies": {
"@noble/curves": "1.1.0",
"@noble/hashes": "1.3.1",
"@scure/bip32": "1.3.1",
"@scure/bip39": "1.2.1"
}
},
"node_modules/ethereum-cryptography/node_modules/@noble/curves": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.1.0.tgz",
"integrity": "sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA==",
"dependencies": {
"@noble/hashes": "1.3.1"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/ethereum-cryptography/node_modules/@noble/hashes": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz",
"integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==",
"engines": {
"node": ">= 16"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/event-iterator": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/event-iterator/-/event-iterator-2.0.0.tgz",
@ -7256,6 +7364,45 @@
"integrity": "sha512-6i/8UoL0P5y4leBIGzvkZdS85RDMG9y1ihZzmTZQ5LdHUYmZ7pKFoj8X0236s3lusPs1Fa5HTQUpwI+UfTcmeA==",
"dev": true
},
"@scure/base": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.3.tgz",
"integrity": "sha512-/+SgoRjLq7Xlf0CWuLHq2LUZeL/w65kfzAPG5NH9pcmBhs+nunQTn4gvdwgMTIXnt9b2C/1SeL2XiysZEyIC9Q=="
},
"@scure/bip32": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.3.1.tgz",
"integrity": "sha512-osvveYtyzdEVbt3OfwwXFr4P2iVBL5u1Q3q4ONBfDY/UpOuXmOlbgwc1xECEboY8wIays8Yt6onaWMUdUbfl0A==",
"requires": {
"@noble/curves": "~1.1.0",
"@noble/hashes": "~1.3.1",
"@scure/base": "~1.1.0"
},
"dependencies": {
"@noble/curves": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.1.0.tgz",
"integrity": "sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA==",
"requires": {
"@noble/hashes": "1.3.1"
}
},
"@noble/hashes": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz",
"integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA=="
}
}
},
"@scure/bip39": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.2.1.tgz",
"integrity": "sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg==",
"requires": {
"@noble/hashes": "~1.3.0",
"@scure/base": "~1.1.0"
}
},
"@swc/helpers": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.2.tgz",
@ -7488,6 +7635,20 @@
"resolved": "https://registry.npmjs.org/@waku/interfaces/-/interfaces-0.0.20.tgz",
"integrity": "sha512-6g2SRCKiAqtxElozXzPNHg68u/lxWSGL1LSXqwA0AAs+WYvK2vYfBM9ceUlbhDEk4ReCUAceUgZgdtdgKGflgA=="
},
"@waku/message-encryption": {
"version": "0.0.23",
"resolved": "https://registry.npmjs.org/@waku/message-encryption/-/message-encryption-0.0.23.tgz",
"integrity": "sha512-i/qMAjO8EVCMlA1BbuGT+uHxzdydYQVXM9mThgH+kTTPTW5gVAMdzFaGmbiHWT6kn3yAsWq5CBB7M3+1zqb8vQ==",
"requires": {
"@noble/secp256k1": "^1.7.1",
"@waku/core": "0.0.25",
"@waku/interfaces": "0.0.20",
"@waku/proto": "0.0.5",
"@waku/utils": "0.0.13",
"debug": "^4.3.4",
"js-sha3": "^0.9.2"
}
},
"@waku/peer-exchange": {
"version": "0.0.18",
"resolved": "https://registry.npmjs.org/@waku/peer-exchange/-/peer-exchange-0.0.18.tgz",
@ -8541,6 +8702,32 @@
"integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
"dev": true
},
"ethereum-cryptography": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.1.2.tgz",
"integrity": "sha512-Z5Ba0T0ImZ8fqXrJbpHcbpAvIswRte2wGNR/KePnu8GbbvgJ47lMxT/ZZPG6i9Jaht4azPDop4HaM00J0J59ug==",
"requires": {
"@noble/curves": "1.1.0",
"@noble/hashes": "1.3.1",
"@scure/bip32": "1.3.1",
"@scure/bip39": "1.2.1"
},
"dependencies": {
"@noble/curves": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.1.0.tgz",
"integrity": "sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA==",
"requires": {
"@noble/hashes": "1.3.1"
}
},
"@noble/hashes": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz",
"integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA=="
}
}
},
"event-iterator": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/event-iterator/-/event-iterator-2.0.0.tgz",

View File

@ -10,7 +10,10 @@
},
"dependencies": {
"@waku/interfaces": "^0.0.20",
"@waku/message-encryption": "^0.0.23",
"@waku/sdk": "^0.0.21",
"@waku/utils": "^0.0.13",
"ethereum-cryptography": "^2.1.2",
"next": "14.0.2",
"react": "^18",
"react-dom": "^18",

View File

@ -0,0 +1,7 @@
export const Loading = () => {
return (
<div className="loading-block">
<p>Loading...</p>
</div>
);
};

View File

@ -2,13 +2,17 @@
import React from "react";
import { waku, Waku, WakuEvents } from "@/services/waku";
import { WakuStatus } from "@/const";
import { Loading } from "./Loading";
type WakuContextProps = {
status: string;
status: WakuStatus;
waku?: Waku;
};
const WakuContext = React.createContext<WakuContextProps>({
status: "",
status: WakuStatus.Initializing,
waku: undefined,
});
type WakuProviderProps = {
@ -16,14 +20,16 @@ type WakuProviderProps = {
};
export const useWaku = () => {
const { status } = React.useContext(WakuContext);
const { status, waku } = React.useContext(WakuContext);
return { status };
return { status, waku };
};
export const WakuProvider = (props: WakuProviderProps) => {
const wakuRef = React.useRef<Waku>();
const [status, setStatus] = React.useState<string>("");
const [status, setStatus] = React.useState<WakuStatus>(
WakuStatus.Initializing
);
React.useEffect(() => {
if (wakuRef.current) {
@ -43,8 +49,16 @@ export const WakuProvider = (props: WakuProviderProps) => {
};
}, [wakuRef, setStatus]);
if (status === WakuStatus.Failed) {
return <>{status}</>;
}
if (status !== WakuStatus.Connected) {
return <Loading />;
}
return (
<WakuContext.Provider value={{ status }}>
<WakuContext.Provider value={{ status, waku: wakuRef.current }}>
{props.children}
</WakuContext.Provider>
);

View File

@ -1,12 +0,0 @@
"use client";
import { useWaku } from "@/app/WakuProvider";
export default function Create() {
const { status } = useWaku();
return (
<div>
<p>{status}</p>
</div>
);
}

View File

@ -1,48 +0,0 @@
"use client";
import React from "react";
import Markdown from "react-markdown";
import { useRouter } from "next/navigation";
import { useNoteHash } from "@/hooks/useNoteHash";
const t = `
### Problem
Cannot read data returned from REST API in browser if request is done with CORS violation.
# Impact
Prevents browsers from querying REST API.
### To reproduce
0. Run nwaku REST API
1. Copy https://github.com/waku-org/waku-frontend/tree/weboko/follow-up
5. Wait for 10 seconds and see in console following error:
It happens even though the request succeds.
### Expected behavior
Error should not happen.
### Additional context
What happens is that browser implements Fetch API in the manner that when request is made to resource with CORS violation then even if it would succeed - client won't be able to read response data.
Spec of the Fetch API - https://fetch.spec.whatwg.org/#concept-filtered-response-opaque
### The fix
Considering we expect REST API to be run only on localhost we should add following HTTP header to allow web apps run on different port to be able to talk to the API.
`;
const View = () => {
const router = useRouter();
const noteHash = useNoteHash();
React.useEffect(() => {
if (!noteHash) {
router.replace("/404");
}
}, [noteHash]);
return <Markdown>{t}</Markdown>;
};
export default View;

View File

@ -0,0 +1,41 @@
* {
box-sizing: border-box;
}
.loading-block {
width: 100vw;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
.note-info {
font-size: 0.8rem;
text-align: right;
}
.create-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.password-value {
font-size: 1rem;
height: 2rem;
width: 250px;
padding: 5px;
}
.save-note {
width: 100px;
height: 50px;
margin-bottom: 10px;
}
.note-value {
width: 100%;
padding: 10px;
min-height: 500px;
}

View File

@ -1,22 +1,79 @@
"use client";
import { useWaku } from "@/app/WakuProvider";
import React from "react";
import { useRouter } from "next/navigation";
import { notes } from "@/services/notes";
export default function Home() {
const { status } = useWaku();
// EgeLwHNbSwIzIz3M
// 3gxFeAHa8sOvTymg
// encrypted nkt698RhpWory0yT
export default function Create() {
const router = useRouter();
const { note, onNoteChange } = useEditNote();
const { password, onPasswordChange } = usePassword();
const onSave = async () => {
if (!note) {
return;
}
try {
const id = await notes.createNote(note, password);
router.push(`/view/${id}`);
} catch (error) {
console.log("Failed to create a note:", error);
}
};
return (
<div>
<p>{status}</p>
<button
onClick={() => {
router.push("/create");
}}
>
Create new
<p className="note-info">
Your record will be stored for couple of days. Markdown is supported.
</p>
<div className="create-header">
<input
className="password-value"
type="password"
onChange={onPasswordChange}
placeholder="Optional password"
autoComplete="off"
/>
<button onClick={onSave} className="save-note">
Save note
</button>
<button>Edit existing</button>
</div>
<textarea
className="note-value"
value={note}
onChange={onNoteChange}
></textarea>
</div>
);
}
const useEditNote = () => {
const [state, setState] = React.useState<string>("");
const onNoteChange = (event: React.FormEvent<HTMLTextAreaElement>) => {
setState(event?.currentTarget?.value);
};
return {
note: state,
onNoteChange,
};
};
const usePassword = () => {
const [password, setPassword] = React.useState<string>();
const onPasswordChange = (event: React.FormEvent<HTMLInputElement>) => {
setPassword(event?.currentTarget?.value);
};
return {
password,
onPasswordChange,
};
};

View File

@ -4,45 +4,28 @@ import React from "react";
import Markdown from "react-markdown";
import { useRouter } from "next/navigation";
import { useNoteHash } from "@/hooks/useNoteHash";
const t = `
### Problem
Cannot read data returned from REST API in browser if request is done with CORS violation.
# Impact
Prevents browsers from querying REST API.
### To reproduce
0. Run nwaku REST API
1. Copy https://github.com/waku-org/waku-frontend/tree/weboko/follow-up
5. Wait for 10 seconds and see in console following error:
It happens even though the request succeds.
### Expected behavior
Error should not happen.
### Additional context
What happens is that browser implements Fetch API in the manner that when request is made to resource with CORS violation then even if it would succeed - client won't be able to read response data.
Spec of the Fetch API - https://fetch.spec.whatwg.org/#concept-filtered-response-opaque
### The fix
Considering we expect REST API to be run only on localhost we should add following HTTP header to allow web apps run on different port to be able to talk to the API.
`;
import { notes } from "@/services/notes";
import { Loading } from "../Loading";
const View = () => {
const router = useRouter();
const noteHash = useNoteHash();
const [note, setNote] = React.useState<string>("");
React.useEffect(() => {
if (!noteHash) {
router.replace("/404");
return;
}
}, [noteHash]);
return <Markdown>{t}</Markdown>;
notes.readNote(noteHash).then((note) => setNote(note || ""));
}, [noteHash, setNote]);
if (!note) {
return <Loading />;
}
return <Markdown>{note}</Markdown>;
};
export default View;

View File

@ -0,0 +1,8 @@
export const CONTENT_TOPIC = "/flush-notes/1/note/proto";
export enum WakuStatus {
Initializing = "Initializing...",
WaitingForPeers = "Waiting for peers...",
Connected = "Connected",
Failed = "Failed to initialize(see logs)",
}

View File

@ -0,0 +1,146 @@
"use client";
import { waku } from "@/services/waku";
import { CONTENT_TOPIC } from "@/const";
import { encrypt, decrypt } from "ethereum-cryptography/aes";
import { getRandomBytes } from "ethereum-cryptography/random";
import { pbkdf2 } from "ethereum-cryptography/pbkdf2";
import {
createDecoder,
createEncoder,
Decoder,
Encoder,
IDecodedMessage,
Unsubscribe,
utf8ToBytes,
bytesToUtf8,
} from "@waku/sdk";
import { generateRandomString } from "@/utils";
import { bytesToHex, hexToBytes } from "@waku/utils/bytes";
type Note = {
id: string;
content: string;
kdf:
| undefined
| {
iv: string;
dklen: number;
c: number;
salt: string;
};
};
export class Notes {
private decoder: Decoder;
private encoder: Encoder;
private messages: IDecodedMessage[] = [];
private subscription: undefined | Unsubscribe;
constructor() {
this.decoder = createDecoder(CONTENT_TOPIC);
this.encoder = createEncoder({ contentTopic: CONTENT_TOPIC });
}
public async createNote(content: string, password?: string): Promise<string> {
const note = password
? await this.encryptNote(content, password)
: { id: generateRandomString(), content, kdf: undefined };
await waku.send(this.encoder, {
payload: utf8ToBytes(JSON.stringify(note)),
});
return note.id;
}
public async readNote(id: string): Promise<string | undefined> {
await this.initMessages();
const message = this.messages
.map((m) => {
try {
return JSON.parse(bytesToUtf8(m.payload)) as Note;
} catch (error) {
console.log("Failed to read message:", error);
}
})
.find((v) => {
if (v?.id === id) {
return true;
}
});
if (!message?.kdf) {
return message?.content;
}
const password = window.prompt("This note is encrypted, need password:");
if (!password) {
console.log("No password was provided, stopping reading a note.");
return;
}
return this.decryptNote(message, password);
}
private async initMessages() {
if (this.subscription) {
return;
}
this.messages = await waku.getHistory(this.decoder);
this.subscription = await waku.subscribe(this.decoder, (message) => {
this.messages.push(message);
});
}
private async encryptNote(content: string, password: string): Promise<Note> {
const iv = await getRandomBytes(16);
const salt = await getRandomBytes(32);
const c = 131072;
const dklen = 16;
const kdf = await pbkdf2(
utf8ToBytes(password.normalize("NFKC")),
salt,
c,
dklen,
"sha256"
);
const encryptedContent = await encrypt(utf8ToBytes(content), kdf, iv);
return {
id: generateRandomString(),
content: bytesToHex(encryptedContent),
kdf: {
c,
dklen,
iv: bytesToHex(iv),
salt: bytesToHex(salt),
},
};
}
private async decryptNote(note: Note, password: string): Promise<string> {
if (!note?.kdf) {
throw Error("Failed to decrypt a note, no kdf params found.");
}
const iv = hexToBytes(note.kdf.iv);
const salt = hexToBytes(note.kdf.salt);
const kdf = await pbkdf2(
utf8ToBytes(password.normalize("NFKC")),
salt,
note.kdf.c,
note.kdf.dklen,
"sha256"
);
const decryptedContent = await decrypt(hexToBytes(note.content), kdf, iv);
return bytesToUtf8(decryptedContent);
}
}
export const notes = new Notes();

View File

@ -1,6 +1,15 @@
"use client";
import { LightNode, createLightNode, waitForRemotePeer } from "@waku/sdk";
import { WakuStatus } from "@/const";
import {
IDecoder,
IEncoder,
IMessage,
LightNode,
createLightNode,
waitForRemotePeer,
IDecodedMessage,
} from "@waku/sdk";
type EventListener = (event: CustomEvent) => void;
@ -24,17 +33,17 @@ export class Waku {
this.initializing = true;
try {
this.emitStatusEvent("Initializing...");
this.emitStatusEvent(WakuStatus.Initializing);
const node = await createLightNode({ defaultBootstrap: true });
await node.start();
this.emitStatusEvent("Waiting for peers...");
this.emitStatusEvent(WakuStatus.WaitingForPeers);
await waitForRemotePeer(node);
this.node = node;
this.initialized = true;
this.emitStatusEvent("Connected");
this.emitStatusEvent(WakuStatus.Connected);
} catch (error) {
console.error("Failed to initialize Waku node:", error);
this.emitStatusEvent("Failed to initialize(see logs)");
this.emitStatusEvent(WakuStatus.Failed);
}
this.initializing = false;
}
@ -47,11 +56,50 @@ export class Waku {
return this.emitter.removeEventListener(event, fn as any);
}
public send(encoder: IEncoder, message: IMessage) {
this.ensureWakuInitialized();
return this.node?.lightPush.send(encoder, message);
}
public async getHistory(
decoder: IDecoder<IDecodedMessage>
): Promise<IDecodedMessage[]> {
this.ensureWakuInitialized();
let messages: IDecodedMessage[] = [];
for await (const promises of this.node!.store.queryGenerator([decoder])) {
const messagesRaw = await Promise.all(promises);
const filteredMessages = messagesRaw.filter(
(v): v is IDecodedMessage => !!v
);
messages = [...messages, ...filteredMessages];
}
return messages;
}
public async subscribe(
decoder: IDecoder<IDecodedMessage>,
fn: (m: IDecodedMessage) => void
) {
this.ensureWakuInitialized();
return this.node!.filter.subscribe(decoder, fn);
}
private emitStatusEvent(payload: string) {
this.emitter.dispatchEvent(
new CustomEvent(WakuEvents.Status, { detail: payload })
);
}
private ensureWakuInitialized() {
if (!waku.initialized) {
const message = "Waku is not initialized.";
console.log(message);
throw Error(message);
}
}
}
export const waku = new Waku();

View File

@ -0,0 +1,10 @@
export function generateRandomString(): string {
let result = "";
let characters =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
let charactersLength = characters.length;
for (let i = 0; i < 16; i++) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return result;
}