From d6d548a09e753d4557ef14bbb0c8808df46ab138 Mon Sep 17 00:00:00 2001 From: Franck Royer Date: Thu, 24 Jun 2021 15:53:27 +1000 Subject: [PATCH 01/20] Save private key in storage In clear for now. --- examples/eth-dm/src/App.tsx | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/examples/eth-dm/src/App.tsx b/examples/eth-dm/src/App.tsx index dcad23c0e3..526178f4a5 100644 --- a/examples/eth-dm/src/App.tsx +++ b/examples/eth-dm/src/App.tsx @@ -21,16 +21,25 @@ import { SendMessage } from './SendMessage'; export const PublicKeyContentTopic = '/eth-dm/1/public-key/json'; export const DirectMessageContentTopic = '/eth-dm/1/direct-message/json'; +const EthDmKeyStorageKey = 'ethDmKey'; + declare let window: any; function App() { const [waku, setWaku] = useState(); const [provider, setProvider] = useState(); - const [ethDmKeyPair, setEthDmKeyPair] = useState(); + const [ethDmKeyPair, setEthDmKeyPair] = useState( + retrieveKeysFromStorage + ); const [publicKeyMsg, setPublicKeyMsg] = useState(); const [publicKeys, setPublicKeys] = useState>(new Map()); const [messages, setMessages] = useState([]); + useEffect(() => { + if (!ethDmKeyPair) return; + saveKeysToStorage(ethDmKeyPair); + }, [ethDmKeyPair]); + useEffect(() => { if (provider) return; try { @@ -233,3 +242,22 @@ async function handleDirectMessage( return copy; }); } + +function saveKeysToStorage(ethDmKeyPair: KeyPair) { + // /!\ Bad idea to store keys in clear. At least put a password on it. + localStorage.setItem(EthDmKeyStorageKey, ethDmKeyPair.privateKey); +} + +function retrieveKeysFromStorage() { + const privateKey = window.localStorage.getItem(EthDmKeyStorageKey); + if (privateKey) { + const publicKey = EthCrypto.publicKeyByPrivateKey(privateKey); + const address = EthCrypto.publicKey.toAddress(publicKey); + return { + privateKey, + publicKey, + address, + }; + } + return; +} From bd0ad81d17be0eb8c60e93eb1f6caf79157b7090 Mon Sep 17 00:00:00 2001 From: Franck Royer Date: Thu, 24 Jun 2021 15:53:42 +1000 Subject: [PATCH 02/20] Disable generate button if key is already present --- examples/eth-dm/src/App.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/eth-dm/src/App.tsx b/examples/eth-dm/src/App.tsx index 526178f4a5..ab6273e5cf 100644 --- a/examples/eth-dm/src/App.tsx +++ b/examples/eth-dm/src/App.tsx @@ -153,7 +153,7 @@ function App() { variant="contained" color="primary" onClick={generateKeyPair} - disabled={!provider} + disabled={!provider || !!ethDmKeyPair} > Generate Eth-DM Key Pair From 93665feac88aec1ee57cb0975c5687b50b1628fc Mon Sep 17 00:00:00 2001 From: Franck Royer Date: Thu, 24 Jun 2021 15:59:20 +1000 Subject: [PATCH 03/20] Generate fresh new keypair, do not use signature as entropy --- examples/eth-dm/src/App.tsx | 2 +- examples/eth-dm/src/crypto.ts | 14 ++------------ 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/examples/eth-dm/src/App.tsx b/examples/eth-dm/src/App.tsx index ab6273e5cf..b94b5f9397 100644 --- a/examples/eth-dm/src/App.tsx +++ b/examples/eth-dm/src/App.tsx @@ -67,7 +67,7 @@ function App() { if (ethDmKeyPair) return; if (!provider) return; - generateEthDmKeyPair(provider.getSigner()) + generateEthDmKeyPair() .then((keyPair) => { setEthDmKeyPair(keyPair); }) diff --git a/examples/eth-dm/src/crypto.ts b/examples/eth-dm/src/crypto.ts index 08a32f8732..4ed906d169 100644 --- a/examples/eth-dm/src/crypto.ts +++ b/examples/eth-dm/src/crypto.ts @@ -5,9 +5,6 @@ import { ethers } from 'ethers'; import { Signer } from '@ethersproject/abstract-signer'; import { PublicKeyMessage } from './messages'; -const Salt = - 'Salt for Eth-Dm, do not share a signature of this message or others could decrypt your messages'; - export interface KeyPair { privateKey: string; publicKey: string; @@ -19,15 +16,8 @@ export interface KeyPair { * the entropy for the EthCrypto keypair. Note that the entropy is hashed with keccak256 * to make the private key. */ -export async function generateEthDmKeyPair( - web3Signer: Signer -): Promise { - const signature = await web3Signer.signMessage(Salt); - // Need to remove '0x' prefix to allow buffer to decode the hex string. - const sigBuf = Buffer.from(signature.slice(2), 'hex'); - const entropy = Buffer.concat([sigBuf, sigBuf]); - const keys = EthCrypto.createIdentity(entropy); - return keys; +export async function generateEthDmKeyPair(): Promise { + return EthCrypto.createIdentity(); } /** From 452f4285eb7d7e374710cbe0a3401520042b7c63 Mon Sep 17 00:00:00 2001 From: Franck Royer Date: Thu, 24 Jun 2021 16:05:22 +1000 Subject: [PATCH 04/20] Move most EthCrypto usage to crypto.ts --- examples/eth-dm/src/App.tsx | 16 ++++--------- examples/eth-dm/src/SendMessage.tsx | 4 ++-- examples/eth-dm/src/crypto.ts | 35 ++++++++++++++++++++++++++--- 3 files changed, 38 insertions(+), 17 deletions(-) diff --git a/examples/eth-dm/src/App.tsx b/examples/eth-dm/src/App.tsx index b94b5f9397..8add3f6467 100644 --- a/examples/eth-dm/src/App.tsx +++ b/examples/eth-dm/src/App.tsx @@ -7,11 +7,12 @@ import { ethers } from 'ethers'; import { Web3Provider } from '@ethersproject/providers'; import { createPublicKeyMessage, + decryptMessage, generateEthDmKeyPair, KeyPair, + recoverKeysFromPrivateKey, validatePublicKeyMessage, } from './crypto'; -import * as EthCrypto from 'eth-crypto'; import { decode, DirectMessage, encode, PublicKeyMessage } from './messages'; import { Message, Messages } from './Messages'; import 'fontsource-roboto'; @@ -225,10 +226,7 @@ async function handleDirectMessage( console.log('Waku Message received:', wakuMsg); if (!wakuMsg.payload) return; const directMessage: DirectMessage = decode(wakuMsg.payload); - const text = await EthCrypto.decryptWithPrivateKey( - privateKey, - directMessage.encMessage - ); + const text = await decryptMessage(privateKey, directMessage); const timestamp = wakuMsg.timestamp ? wakuMsg.timestamp : new Date(); @@ -251,13 +249,7 @@ function saveKeysToStorage(ethDmKeyPair: KeyPair) { function retrieveKeysFromStorage() { const privateKey = window.localStorage.getItem(EthDmKeyStorageKey); if (privateKey) { - const publicKey = EthCrypto.publicKeyByPrivateKey(privateKey); - const address = EthCrypto.publicKey.toAddress(publicKey); - return { - privateKey, - publicKey, - address, - }; + return recoverKeysFromPrivateKey(privateKey); } return; } diff --git a/examples/eth-dm/src/SendMessage.tsx b/examples/eth-dm/src/SendMessage.tsx index f35bd2c4de..28504a2fbb 100644 --- a/examples/eth-dm/src/SendMessage.tsx +++ b/examples/eth-dm/src/SendMessage.tsx @@ -8,9 +8,9 @@ import { } from '@material-ui/core'; import React, { ChangeEvent, useState, KeyboardEvent } from 'react'; import { Waku, WakuMessage } from 'js-waku'; -import * as EthCrypto from 'eth-crypto'; import { DirectMessage, encode } from './messages'; import { DirectMessageContentTopic } from './App'; +import { encryptMessage } from './crypto'; const useStyles = makeStyles((theme) => ({ formControl: { @@ -111,7 +111,7 @@ async function encodeEncryptedWakuMessage( publicKey: string, address: string ): Promise { - const encryptedMsg = await EthCrypto.encryptWithPublicKey(publicKey, message); + const encryptedMsg = await encryptMessage(publicKey, message); const directMsg: DirectMessage = { toAddress: address, diff --git a/examples/eth-dm/src/crypto.ts b/examples/eth-dm/src/crypto.ts index 4ed906d169..c4d87e8ee4 100644 --- a/examples/eth-dm/src/crypto.ts +++ b/examples/eth-dm/src/crypto.ts @@ -3,7 +3,7 @@ import '@ethersproject/shims'; import * as EthCrypto from 'eth-crypto'; import { ethers } from 'ethers'; import { Signer } from '@ethersproject/abstract-signer'; -import { PublicKeyMessage } from './messages'; +import { DirectMessage, PublicKeyMessage } from './messages'; export interface KeyPair { privateKey: string; @@ -59,8 +59,37 @@ export function validatePublicKeyMessage(msg: PublicKeyMessage): boolean { * context. */ function formatPublicKeyForSignature(ethDmPublicKey: string): string { - const txt = JSON.stringify({ + return JSON.stringify({ ethDmPublicKey, }); - return txt; +} + +/** + * Decrypt a Direct Message using the private key. + */ +export function decryptMessage( + privateKey: string, + directMessage: DirectMessage +) { + return EthCrypto.decryptWithPrivateKey(privateKey, directMessage.encMessage); +} + +/** + * Recover Public Key and address from Private Key + */ +export function recoverKeysFromPrivateKey(privateKey: string) { + const publicKey = EthCrypto.publicKeyByPrivateKey(privateKey); + const address = EthCrypto.publicKey.toAddress(publicKey); + return { + privateKey, + publicKey, + address, + }; +} + +/** + * Encrypt message with given Public Key + */ +export async function encryptMessage(publicKey: string, message: string) { + return await EthCrypto.encryptWithPublicKey(publicKey, message); } From 820307ef8c5757d80a7f068a95ea7f30bf73515d Mon Sep 17 00:00:00 2001 From: Franck Royer Date: Fri, 25 Jun 2021 16:17:42 +1000 Subject: [PATCH 05/20] Do not list own public key as recipient --- examples/eth-dm/src/App.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/examples/eth-dm/src/App.tsx b/examples/eth-dm/src/App.tsx index 8add3f6467..8bf74810f7 100644 --- a/examples/eth-dm/src/App.tsx +++ b/examples/eth-dm/src/App.tsx @@ -79,6 +79,7 @@ function App() { const observerPublicKeyMessage = handlePublicKeyMessage.bind( {}, + ethDmKeyPair?.publicKey, setPublicKeys ); @@ -204,11 +205,13 @@ function encodePublicKeyWakuMessage(ethDmMsg: PublicKeyMessage): WakuMessage { } function handlePublicKeyMessage( + myPublicKey: string | undefined, setter: Dispatch>>, msg: WakuMessage ) { if (!msg.payload) return; const publicKeyMsg: PublicKeyMessage = decode(msg.payload); + if (publicKeyMsg.ethDmPublicKey === myPublicKey) return; const res = validatePublicKeyMessage(publicKeyMsg); console.log(`Public Key Message Received, valid: ${res}`, publicKeyMsg); From 47a27a0969d8363b423eb7a9b2589999d3476085 Mon Sep 17 00:00:00 2001 From: Franck Royer Date: Mon, 28 Jun 2021 13:54:22 +1000 Subject: [PATCH 06/20] Replace deprecated method --- examples/eth-dm/src/App.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/eth-dm/src/App.tsx b/examples/eth-dm/src/App.tsx index 8bf74810f7..d35887e871 100644 --- a/examples/eth-dm/src/App.tsx +++ b/examples/eth-dm/src/App.tsx @@ -44,7 +44,7 @@ function App() { useEffect(() => { if (provider) return; try { - window.ethereum.enable(); + window.ethereum.request({ method: 'eth_requestAccounts' }); const _provider = new ethers.providers.Web3Provider(window.ethereum); setProvider(_provider); } catch (e) { From 9a68cc2a86f2523eefcb2c0d88fc993f19b14d33 Mon Sep 17 00:00:00 2001 From: Franck Royer Date: Mon, 28 Jun 2021 15:09:32 +1000 Subject: [PATCH 07/20] Encrypt KeyPair before saving to storage --- examples/eth-dm/src/App.tsx | 57 +++++----- examples/eth-dm/src/LoadKeyFromStorage.tsx | 56 ++++++++++ examples/eth-dm/src/SaveKeyToStorage.tsx | 53 +++++++++ examples/eth-dm/src/crypto.ts | 13 --- examples/eth-dm/src/keyStorage.ts | 121 +++++++++++++++++++++ 5 files changed, 259 insertions(+), 41 deletions(-) create mode 100644 examples/eth-dm/src/LoadKeyFromStorage.tsx create mode 100644 examples/eth-dm/src/SaveKeyToStorage.tsx create mode 100644 examples/eth-dm/src/keyStorage.ts diff --git a/examples/eth-dm/src/App.tsx b/examples/eth-dm/src/App.tsx index d35887e871..011b7d01e0 100644 --- a/examples/eth-dm/src/App.tsx +++ b/examples/eth-dm/src/App.tsx @@ -10,7 +10,6 @@ import { decryptMessage, generateEthDmKeyPair, KeyPair, - recoverKeysFromPrivateKey, validatePublicKeyMessage, } from './crypto'; import { decode, DirectMessage, encode, PublicKeyMessage } from './messages'; @@ -18,29 +17,22 @@ import { Message, Messages } from './Messages'; import 'fontsource-roboto'; import { Button } from '@material-ui/core'; import { SendMessage } from './SendMessage'; +import { SaveKeyToStorage } from './SaveKeyToStorage'; +import { LoadKeyFromStorage } from './LoadKeyFromStorage'; export const PublicKeyContentTopic = '/eth-dm/1/public-key/json'; export const DirectMessageContentTopic = '/eth-dm/1/direct-message/json'; -const EthDmKeyStorageKey = 'ethDmKey'; - declare let window: any; function App() { const [waku, setWaku] = useState(); const [provider, setProvider] = useState(); - const [ethDmKeyPair, setEthDmKeyPair] = useState( - retrieveKeysFromStorage - ); + const [ethDmKeyPair, setEthDmKeyPair] = useState(); const [publicKeyMsg, setPublicKeyMsg] = useState(); const [publicKeys, setPublicKeys] = useState>(new Map()); const [messages, setMessages] = useState([]); - useEffect(() => { - if (!ethDmKeyPair) return; - saveKeysToStorage(ethDmKeyPair); - }, [ethDmKeyPair]); - useEffect(() => { if (provider) return; try { @@ -66,7 +58,6 @@ function App() { const generateKeyPair = () => { if (ethDmKeyPair) return; - if (!provider) return; generateEthDmKeyPair() .then((keyPair) => { @@ -121,7 +112,7 @@ function App() { if (publicKeyMsg) { const wakuMsg = encodePublicKeyWakuMessage(publicKeyMsg); waku.lightPush.push(wakuMsg).catch((e) => { - console.error('Failed to send Public Key Message'); + console.error('Failed to send Public Key Message', e); }); } else { createPublicKeyMessage(provider.getSigner(), ethDmKeyPair.publicKey) @@ -129,7 +120,7 @@ function App() { setPublicKeyMsg(msg); const wakuMsg = encodePublicKeyWakuMessage(msg); waku.lightPush.push(wakuMsg).catch((e) => { - console.error('Failed to send Public Key Message'); + console.error('Failed to send Public Key Message', e); }); }) .catch((e) => { @@ -155,10 +146,33 @@ function App() { variant="contained" color="primary" onClick={generateKeyPair} - disabled={!provider || !!ethDmKeyPair} + disabled={!!ethDmKeyPair} > Generate Eth-DM Key Pair + +
+ setEthDmKeyPair(keyPair)} + disabled={!!ethDmKeyPair} + /> +
+
+ +
+
+
+ ); +} diff --git a/examples/eth-dm/src/SaveKeyToStorage.tsx b/examples/eth-dm/src/SaveKeyToStorage.tsx new file mode 100644 index 0000000000..eaaf4bbd90 --- /dev/null +++ b/examples/eth-dm/src/SaveKeyToStorage.tsx @@ -0,0 +1,53 @@ +import { Button, TextField } from '@material-ui/core'; +import React, { ChangeEvent, useState } from 'react'; +import { KeyPair } from './crypto'; +import { saveKeyPairToStorage } from './keyStorage'; + +export interface Props { + ethDmKeyPair: KeyPair | undefined; +} + +export function SaveKeyToStorage(props: Props) { + const [password, setPassword] = useState(); + + const ethDmKeyPair = props.ethDmKeyPair; + + const handlePasswordChange = (event: ChangeEvent) => { + setPassword(event.target.value); + }; + + const saveKeyPair = () => { + if (!ethDmKeyPair) return; + if (!password) return; + saveKeyPairToStorage(ethDmKeyPair, password).then(() => { + console.log('EthDm KeyPair saved to storage'); + }); + }; + + return ( +
+ + +
+ ); +} diff --git a/examples/eth-dm/src/crypto.ts b/examples/eth-dm/src/crypto.ts index c4d87e8ee4..94535643a4 100644 --- a/examples/eth-dm/src/crypto.ts +++ b/examples/eth-dm/src/crypto.ts @@ -74,19 +74,6 @@ export function decryptMessage( return EthCrypto.decryptWithPrivateKey(privateKey, directMessage.encMessage); } -/** - * Recover Public Key and address from Private Key - */ -export function recoverKeysFromPrivateKey(privateKey: string) { - const publicKey = EthCrypto.publicKeyByPrivateKey(privateKey); - const address = EthCrypto.publicKey.toAddress(publicKey); - return { - privateKey, - publicKey, - address, - }; -} - /** * Encrypt message with given Public Key */ diff --git a/examples/eth-dm/src/keyStorage.ts b/examples/eth-dm/src/keyStorage.ts new file mode 100644 index 0000000000..5ed46de9f5 --- /dev/null +++ b/examples/eth-dm/src/keyStorage.ts @@ -0,0 +1,121 @@ +import { KeyPair } from './crypto'; + +/** + * Save keypair to storage, encrypted with password + */ +export async function saveKeyPairToStorage( + ethDmKeyPair: KeyPair, + password: string +) { + const { salt, iv, cipher } = await encryptKey(ethDmKeyPair, password); + + const data = { + salt: new Buffer(salt).toString('hex'), + iv: new Buffer(iv).toString('hex'), + cipher: new Buffer(cipher).toString('hex'), + }; + + localStorage.setItem('cipherEthDmKeyPair', JSON.stringify(data)); +} + +/** + * Load keypair from storage, decrypted using password + */ +export async function loadKeyPairFromStorage( + password: string +): Promise { + const str = localStorage.getItem('cipherEthDmKeyPair'); + if (!str) return; + const data = JSON.parse(str); + + const salt = new Buffer(data.salt, 'hex'); + const iv = new Buffer(data.iv, 'hex'); + const cipher = new Buffer(data.cipher, 'hex'); + + return await decryptKey(salt, iv, cipher, password); +} + +/** + * Use password user as key material for wrap key. + */ +function getKeyMaterial(password: string): Promise { + const enc = new TextEncoder(); + return window.crypto.subtle.importKey( + 'raw', + enc.encode(password), + { name: 'PBKDF2' }, + false, + ['deriveBits', 'deriveKey'] + ); +} + +/** + * get key to store password + */ +function getWrapKey(keyMaterial: CryptoKey, salt: Uint8Array) { + return window.crypto.subtle.deriveKey( + { + name: 'PBKDF2', + salt: salt, + iterations: 100000, + hash: 'SHA-256', + }, + keyMaterial, + { name: 'AES-GCM', length: 256 }, + true, + ['encrypt', 'decrypt'] + ); +} + +/** + * Encrypt Eth-DM KeyPair using provided password + */ +async function encryptKey(ethDmKeyPair: KeyPair, password: string) { + const keyMaterial = await getKeyMaterial(password); + const salt = window.crypto.getRandomValues(new Uint8Array(16)); + const wrappingKey = await getWrapKey(keyMaterial, salt); + + const enc = new TextEncoder(); + const encodedKeyPair = enc.encode(JSON.stringify(ethDmKeyPair)); + + const iv = window.crypto.getRandomValues(new Uint8Array(12)); + const cipher = await window.crypto.subtle.encrypt( + { + name: 'AES-GCM', + iv: iv, + }, + wrappingKey, + encodedKeyPair + ); + + return { salt, iv, cipher }; +} + +/** + * Derive a key from a password, and use the key to decrypt the cipher key pair. + */ +async function decryptKey( + salt: Buffer, + iv: Buffer, + cipherKeyPair: Buffer, + password: string +): Promise { + const keyMaterial = await getKeyMaterial(password); + const key = await getWrapKey(keyMaterial, salt); + + try { + let decrypted = await window.crypto.subtle.decrypt( + { + name: 'AES-GCM', + iv: iv, + }, + key, + cipherKeyPair + ); + + let dec = new TextDecoder(); + return JSON.parse(dec.decode(decrypted)); + } catch (e) { + return; + } +} From f9152f24b06118d3465a4655a8fa1a67e0ee7a70 Mon Sep 17 00:00:00 2001 From: Franck Royer Date: Tue, 29 Jun 2021 11:52:48 +1000 Subject: [PATCH 08/20] Move key handling modules to common dir --- examples/eth-dm/src/App.tsx | 4 ++-- examples/eth-dm/src/{ => key_handling}/LoadKeyFromStorage.tsx | 4 ++-- examples/eth-dm/src/{ => key_handling}/SaveKeyToStorage.tsx | 4 ++-- .../eth-dm/src/{keyStorage.ts => key_handling/key_storage.ts} | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) rename examples/eth-dm/src/{ => key_handling}/LoadKeyFromStorage.tsx (93%) rename examples/eth-dm/src/{ => key_handling}/SaveKeyToStorage.tsx (92%) rename examples/eth-dm/src/{keyStorage.ts => key_handling/key_storage.ts} (98%) diff --git a/examples/eth-dm/src/App.tsx b/examples/eth-dm/src/App.tsx index 011b7d01e0..222586520f 100644 --- a/examples/eth-dm/src/App.tsx +++ b/examples/eth-dm/src/App.tsx @@ -17,8 +17,8 @@ import { Message, Messages } from './Messages'; import 'fontsource-roboto'; import { Button } from '@material-ui/core'; import { SendMessage } from './SendMessage'; -import { SaveKeyToStorage } from './SaveKeyToStorage'; -import { LoadKeyFromStorage } from './LoadKeyFromStorage'; +import { SaveKeyToStorage } from './key_handling/SaveKeyToStorage'; +import { LoadKeyFromStorage } from './key_handling/LoadKeyFromStorage'; export const PublicKeyContentTopic = '/eth-dm/1/public-key/json'; export const DirectMessageContentTopic = '/eth-dm/1/direct-message/json'; diff --git a/examples/eth-dm/src/LoadKeyFromStorage.tsx b/examples/eth-dm/src/key_handling/LoadKeyFromStorage.tsx similarity index 93% rename from examples/eth-dm/src/LoadKeyFromStorage.tsx rename to examples/eth-dm/src/key_handling/LoadKeyFromStorage.tsx index 96d625eb0d..d3022b9377 100644 --- a/examples/eth-dm/src/LoadKeyFromStorage.tsx +++ b/examples/eth-dm/src/key_handling/LoadKeyFromStorage.tsx @@ -1,7 +1,7 @@ import { Button, TextField } from '@material-ui/core'; import React, { ChangeEvent, useState } from 'react'; -import { loadKeyPairFromStorage } from './keyStorage'; -import { KeyPair } from './crypto'; +import { loadKeyPairFromStorage } from './key_storage'; +import { KeyPair } from '../crypto'; export interface Props { setEthDmKeyPair: (keyPair: KeyPair) => void; diff --git a/examples/eth-dm/src/SaveKeyToStorage.tsx b/examples/eth-dm/src/key_handling/SaveKeyToStorage.tsx similarity index 92% rename from examples/eth-dm/src/SaveKeyToStorage.tsx rename to examples/eth-dm/src/key_handling/SaveKeyToStorage.tsx index eaaf4bbd90..e57287d1a9 100644 --- a/examples/eth-dm/src/SaveKeyToStorage.tsx +++ b/examples/eth-dm/src/key_handling/SaveKeyToStorage.tsx @@ -1,7 +1,7 @@ import { Button, TextField } from '@material-ui/core'; import React, { ChangeEvent, useState } from 'react'; -import { KeyPair } from './crypto'; -import { saveKeyPairToStorage } from './keyStorage'; +import { KeyPair } from '../crypto'; +import { saveKeyPairToStorage } from './key_storage'; export interface Props { ethDmKeyPair: KeyPair | undefined; diff --git a/examples/eth-dm/src/keyStorage.ts b/examples/eth-dm/src/key_handling/key_storage.ts similarity index 98% rename from examples/eth-dm/src/keyStorage.ts rename to examples/eth-dm/src/key_handling/key_storage.ts index 5ed46de9f5..d6fc7d2da6 100644 --- a/examples/eth-dm/src/keyStorage.ts +++ b/examples/eth-dm/src/key_handling/key_storage.ts @@ -1,4 +1,4 @@ -import { KeyPair } from './crypto'; +import { KeyPair } from '../crypto'; /** * Save keypair to storage, encrypted with password From b677b0eb7bb14e15a984730524e2c65b48372ae3 Mon Sep 17 00:00:00 2001 From: Franck Royer Date: Tue, 29 Jun 2021 11:56:49 +1000 Subject: [PATCH 09/20] Use Key Pair terminology for eth-dm keypair --- examples/eth-dm/src/App.tsx | 8 ++++---- .../LoadKeyPair.tsx} | 4 ++-- .../SaveKeyPair.tsx} | 4 ++-- .../key_pair_storage.ts} | 0 4 files changed, 8 insertions(+), 8 deletions(-) rename examples/eth-dm/src/{key_handling/LoadKeyFromStorage.tsx => key_pair_handling/LoadKeyPair.tsx} (92%) rename examples/eth-dm/src/{key_handling/SaveKeyToStorage.tsx => key_pair_handling/SaveKeyPair.tsx} (92%) rename examples/eth-dm/src/{key_handling/key_storage.ts => key_pair_handling/key_pair_storage.ts} (100%) diff --git a/examples/eth-dm/src/App.tsx b/examples/eth-dm/src/App.tsx index 222586520f..10bec9feb3 100644 --- a/examples/eth-dm/src/App.tsx +++ b/examples/eth-dm/src/App.tsx @@ -17,8 +17,8 @@ import { Message, Messages } from './Messages'; import 'fontsource-roboto'; import { Button } from '@material-ui/core'; import { SendMessage } from './SendMessage'; -import { SaveKeyToStorage } from './key_handling/SaveKeyToStorage'; -import { LoadKeyFromStorage } from './key_handling/LoadKeyFromStorage'; +import { SaveKeyPair } from './key_pair_handling/SaveKeyPair'; +import { LoadKeyPair } from './key_pair_handling/LoadKeyPair'; export const PublicKeyContentTopic = '/eth-dm/1/public-key/json'; export const DirectMessageContentTopic = '/eth-dm/1/direct-message/json'; @@ -158,7 +158,7 @@ function App() { flexWrap: 'wrap', }} > - setEthDmKeyPair(keyPair)} disabled={!!ethDmKeyPair} /> @@ -170,7 +170,7 @@ function App() { flexWrap: 'wrap', }} > - +
-
-
- setEthDmKeyPair(keyPair)} - disabled={!!ethDmKeyPair} - /> -
-
- -
+ setEthDmKeyPair(keyPair)} + />
+
+
+ setEthDmKeyPair(keyPair)} + disabled={!!ethDmKeyPair} + /> +
+
+ +
+ + ); +} From 5de030d3ca4b33bf3f732368cc02fb623cb6ccb2 Mon Sep 17 00:00:00 2001 From: Franck Royer Date: Tue, 29 Jun 2021 12:10:24 +1000 Subject: [PATCH 11/20] Deconstruct properties for more concise code --- examples/eth-dm/src/SendMessage.tsx | 8 +++----- examples/eth-dm/src/key_pair_handling/LoadKeyPair.tsx | 6 ++---- examples/eth-dm/src/key_pair_handling/SaveKeyPair.tsx | 4 +--- 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/examples/eth-dm/src/SendMessage.tsx b/examples/eth-dm/src/SendMessage.tsx index 28504a2fbb..95de6cb41f 100644 --- a/examples/eth-dm/src/SendMessage.tsx +++ b/examples/eth-dm/src/SendMessage.tsx @@ -28,13 +28,11 @@ export interface Props { recipients: Map; } -export function SendMessage(props: Props) { +export function SendMessage({ waku, recipients }: Props) { const classes = useStyles(); const [recipient, setRecipient] = useState(''); const [message, setMessage] = useState(); - const waku = props.waku; - const handleRecipientChange = ( event: ChangeEvent<{ name?: string; value: unknown }> ) => { @@ -45,7 +43,7 @@ export function SendMessage(props: Props) { setMessage(event.target.value); }; - const items = Array.from(props.recipients.keys()).map((recipient) => { + const items = Array.from(recipients.keys()).map((recipient) => { return ( {recipient} @@ -63,7 +61,7 @@ export function SendMessage(props: Props) { if (!waku) return; if (!recipient) return; if (!message) return; - const publicKey = props.recipients.get(recipient); + const publicKey = recipients.get(recipient); if (!publicKey) return; sendMessage(waku, recipient, publicKey, message, (res) => { diff --git a/examples/eth-dm/src/key_pair_handling/LoadKeyPair.tsx b/examples/eth-dm/src/key_pair_handling/LoadKeyPair.tsx index 63739cb6e7..23b864d295 100644 --- a/examples/eth-dm/src/key_pair_handling/LoadKeyPair.tsx +++ b/examples/eth-dm/src/key_pair_handling/LoadKeyPair.tsx @@ -8,11 +8,9 @@ export interface Props { disabled: boolean; } -export function LoadKeyPair(props: Props) { +export function LoadKeyPair({ disabled, setEthDmKeyPair }: Props) { const [password, setPassword] = useState(); - const disabled = props.disabled; - const handlePasswordChange = (event: ChangeEvent) => { setPassword(event.target.value); }; @@ -23,7 +21,7 @@ export function LoadKeyPair(props: Props) { loadKeyPairFromStorage(password).then((keyPair: KeyPair | undefined) => { if (!keyPair) return; console.log('EthDm KeyPair loaded from storage'); - props.setEthDmKeyPair(keyPair); + setEthDmKeyPair(keyPair); }); }; diff --git a/examples/eth-dm/src/key_pair_handling/SaveKeyPair.tsx b/examples/eth-dm/src/key_pair_handling/SaveKeyPair.tsx index 6b2a8543f6..2d476a3a70 100644 --- a/examples/eth-dm/src/key_pair_handling/SaveKeyPair.tsx +++ b/examples/eth-dm/src/key_pair_handling/SaveKeyPair.tsx @@ -7,11 +7,9 @@ export interface Props { ethDmKeyPair: KeyPair | undefined; } -export function SaveKeyPair(props: Props) { +export function SaveKeyPair({ ethDmKeyPair }: Props) { const [password, setPassword] = useState(); - const ethDmKeyPair = props.ethDmKeyPair; - const handlePasswordChange = (event: ChangeEvent) => { setPassword(event.target.value); }; From 6921242877e3e8d0483eb7237c2c365b95be8b52 Mon Sep 17 00:00:00 2001 From: Franck Royer Date: Tue, 29 Jun 2021 12:30:57 +1000 Subject: [PATCH 12/20] Extract waku initialisation in separate component --- examples/eth-dm/src/App.tsx | 140 ++---------------- examples/eth-dm/src/InitWaku.tsx | 137 +++++++++++++++++ examples/eth-dm/src/Messages.tsx | 2 +- examples/eth-dm/src/SendMessage.tsx | 2 +- .../src/key_pair_handling/KeyPairHandling.tsx | 5 +- 5 files changed, 158 insertions(+), 128 deletions(-) create mode 100644 examples/eth-dm/src/InitWaku.tsx diff --git a/examples/eth-dm/src/App.tsx b/examples/eth-dm/src/App.tsx index f93e02842a..4bd9496a9d 100644 --- a/examples/eth-dm/src/App.tsx +++ b/examples/eth-dm/src/App.tsx @@ -1,22 +1,18 @@ import '@ethersproject/shims'; -import React, { Dispatch, SetStateAction, useEffect, useState } from 'react'; +import React, { useEffect, useState } from 'react'; import './App.css'; -import { Environment, getStatusFleetNodes, Waku, WakuMessage } from 'js-waku'; +import { Waku, WakuMessage } from 'js-waku'; import { ethers } from 'ethers'; import { Web3Provider } from '@ethersproject/providers'; -import { - createPublicKeyMessage, - decryptMessage, - KeyPair, - validatePublicKeyMessage, -} from './crypto'; -import { decode, DirectMessage, encode, PublicKeyMessage } from './messages'; -import { Message, Messages } from './Messages'; +import { createPublicKeyMessage, KeyPair } from './crypto'; +import { encode, PublicKeyMessage } from './messages'; +import Messages, { Message } from './Messages'; import 'fontsource-roboto'; import { Button } from '@material-ui/core'; -import { SendMessage } from './SendMessage'; -import { KeyPairHandling } from './key_pair_handling/KeyPairHandling'; +import SendMessage from './SendMessage'; +import KeyPairHandling from './key_pair_handling/KeyPairHandling'; +import InitWaku from './InitWaku'; export const PublicKeyContentTopic = '/eth-dm/1/public-key/json'; export const DirectMessageContentTopic = '/eth-dm/1/direct-message/json'; @@ -42,54 +38,6 @@ function App() { } }, [provider]); - useEffect(() => { - if (waku) return; - initWaku() - .then((wakuNode) => { - console.log('waku: ready'); - setWaku(wakuNode); - }) - .catch((e) => { - console.error('Failed to initiate Waku', e); - }); - }, [waku]); - - const observerPublicKeyMessage = handlePublicKeyMessage.bind( - {}, - ethDmKeyPair?.publicKey, - setPublicKeys - ); - - const observerDirectMessage = ethDmKeyPair - ? handleDirectMessage.bind({}, setMessages, ethDmKeyPair.privateKey) - : undefined; - - useEffect(() => { - if (!waku) return; - waku.relay.addObserver(observerPublicKeyMessage, [PublicKeyContentTopic]); - - return function cleanUp() { - if (!waku) return; - waku.relay.deleteObserver(observerPublicKeyMessage, [ - PublicKeyContentTopic, - ]); - }; - }); - - useEffect(() => { - if (!waku) return; - if (!observerDirectMessage) return; - waku.relay.addObserver(observerDirectMessage, [DirectMessageContentTopic]); - - return function cleanUp() { - if (!waku) return; - if (!observerDirectMessage) return; - waku.relay.deleteObserver(observerDirectMessage, [ - DirectMessageContentTopic, - ]); - }; - }); - const broadcastPublicKey = () => { if (!ethDmKeyPair) return; if (!provider) return; @@ -115,12 +63,16 @@ function App() { } }; - const wakuReady = !!waku ? 'Waku is ready' : 'Waku is loading'; - return (
- {wakuReady} + setEthDmKeyPair(keyPair)} @@ -144,69 +96,7 @@ function App() { export default App; -async function initWaku(): Promise { - const waku = await Waku.create({}); - - const nodes = await getNodes(); - await Promise.all( - nodes.map((addr) => { - return waku.dial(addr); - }) - ); - - return waku; -} - -function getNodes() { - // Works with react-scripts - if (process?.env?.NODE_ENV === 'development') { - return getStatusFleetNodes(Environment.Test); - } else { - return getStatusFleetNodes(Environment.Prod); - } -} - function encodePublicKeyWakuMessage(ethDmMsg: PublicKeyMessage): WakuMessage { const payload = encode(ethDmMsg); return WakuMessage.fromBytes(payload, PublicKeyContentTopic); } - -function handlePublicKeyMessage( - myPublicKey: string | undefined, - setter: Dispatch>>, - msg: WakuMessage -) { - if (!msg.payload) return; - const publicKeyMsg: PublicKeyMessage = decode(msg.payload); - if (publicKeyMsg.ethDmPublicKey === myPublicKey) return; - const res = validatePublicKeyMessage(publicKeyMsg); - console.log(`Public Key Message Received, valid: ${res}`, publicKeyMsg); - - setter((prevPks: Map) => { - prevPks.set(publicKeyMsg.ethAddress, publicKeyMsg.ethDmPublicKey); - return new Map(prevPks); - }); -} - -async function handleDirectMessage( - setter: Dispatch>, - privateKey: string, - wakuMsg: WakuMessage -) { - console.log('Waku Message received:', wakuMsg); - if (!wakuMsg.payload) return; - const directMessage: DirectMessage = decode(wakuMsg.payload); - const text = await decryptMessage(privateKey, directMessage); - - const timestamp = wakuMsg.timestamp ? wakuMsg.timestamp : new Date(); - - console.log('Message decrypted:', text); - setter((prevMsgs: Message[]) => { - const copy = prevMsgs.slice(); - copy.push({ - text: text, - timestamp: timestamp, - }); - return copy; - }); -} diff --git a/examples/eth-dm/src/InitWaku.tsx b/examples/eth-dm/src/InitWaku.tsx new file mode 100644 index 0000000000..4c59e851e9 --- /dev/null +++ b/examples/eth-dm/src/InitWaku.tsx @@ -0,0 +1,137 @@ +import { Dispatch, SetStateAction, useEffect } from 'react'; +import { Environment, getStatusFleetNodes, Waku, WakuMessage } from 'js-waku'; +import { decode, DirectMessage, PublicKeyMessage } from './messages'; +import { decryptMessage, KeyPair, validatePublicKeyMessage } from './crypto'; +import { Message } from './Messages'; +import { DirectMessageContentTopic, PublicKeyContentTopic } from './App'; + +interface Props { + waku: Waku | undefined; + setWaku: (waku: Waku) => void; + ethDmKeyPair: KeyPair | undefined; + setPublicKeys: Dispatch>>; + setMessages: Dispatch>; +} + +/** + * Does all the waku initialisation + */ +export default function InitWaku({ + waku, + setWaku, + ethDmKeyPair, + setPublicKeys, + setMessages, +}: Props) { + useEffect(() => { + if (waku) return; + initWaku() + .then((wakuNode) => { + console.log('waku: ready'); + setWaku(wakuNode); + }) + .catch((e) => { + console.error('Failed to initiate Waku', e); + }); + }, [waku, setWaku]); + + const observerPublicKeyMessage = handlePublicKeyMessage.bind( + {}, + ethDmKeyPair?.publicKey, + setPublicKeys + ); + + const observerDirectMessage = ethDmKeyPair + ? handleDirectMessage.bind({}, setMessages, ethDmKeyPair.privateKey) + : undefined; + + useEffect(() => { + if (!waku) return; + waku.relay.addObserver(observerPublicKeyMessage, [PublicKeyContentTopic]); + + return function cleanUp() { + if (!waku) return; + waku.relay.deleteObserver(observerPublicKeyMessage, [ + PublicKeyContentTopic, + ]); + }; + }); + + useEffect(() => { + if (!waku) return; + if (!observerDirectMessage) return; + waku.relay.addObserver(observerDirectMessage, [DirectMessageContentTopic]); + + return function cleanUp() { + if (!waku) return; + if (!observerDirectMessage) return; + waku.relay.deleteObserver(observerDirectMessage, [ + DirectMessageContentTopic, + ]); + }; + }); + + return

{!!waku ? 'Waku is ready' : 'Waku is loading'}

; +} + +async function initWaku(): Promise { + const waku = await Waku.create({}); + + const nodes = await getNodes(); + await Promise.all( + nodes.map((addr) => { + return waku.dial(addr); + }) + ); + + return waku; +} + +function getNodes() { + // Works with react-scripts + if (process?.env?.NODE_ENV === 'development') { + return getStatusFleetNodes(Environment.Test); + } else { + return getStatusFleetNodes(Environment.Prod); + } +} + +function handlePublicKeyMessage( + myPublicKey: string | undefined, + setter: Dispatch>>, + msg: WakuMessage +) { + if (!msg.payload) return; + const publicKeyMsg: PublicKeyMessage = decode(msg.payload); + if (publicKeyMsg.ethDmPublicKey === myPublicKey) return; + const res = validatePublicKeyMessage(publicKeyMsg); + console.log(`Public Key Message Received, valid: ${res}`, publicKeyMsg); + + setter((prevPks: Map) => { + prevPks.set(publicKeyMsg.ethAddress, publicKeyMsg.ethDmPublicKey); + return new Map(prevPks); + }); +} + +async function handleDirectMessage( + setter: Dispatch>, + privateKey: string, + wakuMsg: WakuMessage +) { + console.log('Waku Message received:', wakuMsg); + if (!wakuMsg.payload) return; + const directMessage: DirectMessage = decode(wakuMsg.payload); + const text = await decryptMessage(privateKey, directMessage); + + const timestamp = wakuMsg.timestamp ? wakuMsg.timestamp : new Date(); + + console.log('Message decrypted:', text); + setter((prevMsgs: Message[]) => { + const copy = prevMsgs.slice(); + copy.push({ + text: text, + timestamp: timestamp, + }); + return copy; + }); +} diff --git a/examples/eth-dm/src/Messages.tsx b/examples/eth-dm/src/Messages.tsx index d12756f91a..1d3384bc4d 100644 --- a/examples/eth-dm/src/Messages.tsx +++ b/examples/eth-dm/src/Messages.tsx @@ -7,7 +7,7 @@ export interface Props { messages: Message[]; } -export function Messages(props: Props) { +export default function Messages(props: Props) { const messages = props.messages.map((msg) => { return (
  • diff --git a/examples/eth-dm/src/SendMessage.tsx b/examples/eth-dm/src/SendMessage.tsx index 95de6cb41f..d38cba8437 100644 --- a/examples/eth-dm/src/SendMessage.tsx +++ b/examples/eth-dm/src/SendMessage.tsx @@ -28,7 +28,7 @@ export interface Props { recipients: Map; } -export function SendMessage({ waku, recipients }: Props) { +export default function SendMessage({ waku, recipients }: Props) { const classes = useStyles(); const [recipient, setRecipient] = useState(''); const [message, setMessage] = useState(); diff --git a/examples/eth-dm/src/key_pair_handling/KeyPairHandling.tsx b/examples/eth-dm/src/key_pair_handling/KeyPairHandling.tsx index 69030649f9..ea22c6a91c 100644 --- a/examples/eth-dm/src/key_pair_handling/KeyPairHandling.tsx +++ b/examples/eth-dm/src/key_pair_handling/KeyPairHandling.tsx @@ -9,7 +9,10 @@ export interface Props { setEthDmKeyPair: (keyPair: KeyPair) => void; } -export function KeyPairHandling({ ethDmKeyPair, setEthDmKeyPair }: Props) { +export default function KeyPairHandling({ + ethDmKeyPair, + setEthDmKeyPair, +}: Props) { const generateKeyPair = () => { if (ethDmKeyPair) return; From 5661c7d1ecc5b5d1aaf6db5ed1c33cd03f503088 Mon Sep 17 00:00:00 2001 From: Franck Royer Date: Tue, 29 Jun 2021 12:46:42 +1000 Subject: [PATCH 13/20] Do not attempt to decrypt other's messages --- examples/eth-dm/src/App.tsx | 9 +++++++++ examples/eth-dm/src/InitWaku.tsx | 17 ++++++++++++++--- examples/eth-dm/src/crypto.ts | 1 - 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/examples/eth-dm/src/App.tsx b/examples/eth-dm/src/App.tsx index 4bd9496a9d..b1580a2859 100644 --- a/examples/eth-dm/src/App.tsx +++ b/examples/eth-dm/src/App.tsx @@ -26,6 +26,7 @@ function App() { const [publicKeyMsg, setPublicKeyMsg] = useState(); const [publicKeys, setPublicKeys] = useState>(new Map()); const [messages, setMessages] = useState([]); + const [address, setAddress] = useState(); useEffect(() => { if (provider) return; @@ -38,6 +39,13 @@ function App() { } }, [provider]); + useEffect(() => { + provider + ?.getSigner() + .getAddress() + .then((address) => setAddress(address)); + }); + const broadcastPublicKey = () => { if (!ethDmKeyPair) return; if (!provider) return; @@ -72,6 +80,7 @@ function App() { setPublicKeys={setPublicKeys} setWaku={setWaku} waku={waku} + address={address} /> >>; setMessages: Dispatch>; + address: string | undefined; } /** @@ -22,6 +23,7 @@ export default function InitWaku({ ethDmKeyPair, setPublicKeys, setMessages, + address, }: Props) { useEffect(() => { if (waku) return; @@ -41,9 +43,15 @@ export default function InitWaku({ setPublicKeys ); - const observerDirectMessage = ethDmKeyPair - ? handleDirectMessage.bind({}, setMessages, ethDmKeyPair.privateKey) - : undefined; + const observerDirectMessage = + ethDmKeyPair && address + ? handleDirectMessage.bind( + {}, + setMessages, + ethDmKeyPair.privateKey, + address + ) + : undefined; useEffect(() => { if (!waku) return; @@ -116,11 +124,14 @@ function handlePublicKeyMessage( async function handleDirectMessage( setter: Dispatch>, privateKey: string, + address: string, wakuMsg: WakuMessage ) { console.log('Waku Message received:', wakuMsg); if (!wakuMsg.payload) return; const directMessage: DirectMessage = decode(wakuMsg.payload); + if (directMessage.toAddress !== address) return; + const text = await decryptMessage(privateKey, directMessage); const timestamp = wakuMsg.timestamp ? wakuMsg.timestamp : new Date(); diff --git a/examples/eth-dm/src/crypto.ts b/examples/eth-dm/src/crypto.ts index 94535643a4..f0db5d213e 100644 --- a/examples/eth-dm/src/crypto.ts +++ b/examples/eth-dm/src/crypto.ts @@ -8,7 +8,6 @@ import { DirectMessage, PublicKeyMessage } from './messages'; export interface KeyPair { privateKey: string; publicKey: string; - address: string; } /** From 31d0efc8d2c8e4e4cd42207ba6cab613b114f4b5 Mon Sep 17 00:00:00 2001 From: Franck Royer Date: Tue, 29 Jun 2021 15:32:29 +1000 Subject: [PATCH 14/20] Organise elements in fieldboxes --- examples/eth-dm/package-lock.json | 31 +++++ examples/eth-dm/package.json | 1 + examples/eth-dm/src/App.tsx | 127 ++++++++++++++---- examples/eth-dm/src/InitWaku.tsx | 2 +- .../src/key_pair_handling/KeyPairHandling.tsx | 90 ++++++++----- .../src/key_pair_handling/LoadKeyPair.tsx | 43 ++---- .../src/key_pair_handling/PasswordInput.tsx | 24 ++++ .../src/key_pair_handling/SaveKeyPair.tsx | 43 ++---- 8 files changed, 232 insertions(+), 129 deletions(-) create mode 100644 examples/eth-dm/src/key_pair_handling/PasswordInput.tsx diff --git a/examples/eth-dm/package-lock.json b/examples/eth-dm/package-lock.json index 875cb74f35..18ca7dca48 100644 --- a/examples/eth-dm/package-lock.json +++ b/examples/eth-dm/package-lock.json @@ -8,6 +8,7 @@ "version": "0.1.0", "dependencies": { "@material-ui/core": "^4.11.4", + "@material-ui/icons": "^4.11.2", "@testing-library/jest-dom": "^5.11.4", "@testing-library/react": "^11.1.0", "@testing-library/user-event": "^12.1.10", @@ -3451,6 +3452,28 @@ } } }, + "node_modules/@material-ui/icons": { + "version": "4.11.2", + "resolved": "https://registry.npmjs.org/@material-ui/icons/-/icons-4.11.2.tgz", + "integrity": "sha512-fQNsKX2TxBmqIGJCSi3tGTO/gZ+eJgWmMJkgDiOfyNaunNaxcklJQFaFogYcFl0qFuaEz1qaXYXboa/bUXVSOQ==", + "dependencies": { + "@babel/runtime": "^7.4.4" + }, + "engines": { + "node": ">=8.0.0" + }, + "peerDependencies": { + "@material-ui/core": "^4.0.0", + "@types/react": "^16.8.6 || ^17.0.0", + "react": "^16.8.0 || ^17.0.0", + "react-dom": "^16.8.0 || ^17.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@material-ui/styles": { "version": "4.11.4", "resolved": "https://registry.npmjs.org/@material-ui/styles/-/styles-4.11.4.tgz", @@ -26510,6 +26533,14 @@ "react-transition-group": "^4.4.0" } }, + "@material-ui/icons": { + "version": "4.11.2", + "resolved": "https://registry.npmjs.org/@material-ui/icons/-/icons-4.11.2.tgz", + "integrity": "sha512-fQNsKX2TxBmqIGJCSi3tGTO/gZ+eJgWmMJkgDiOfyNaunNaxcklJQFaFogYcFl0qFuaEz1qaXYXboa/bUXVSOQ==", + "requires": { + "@babel/runtime": "^7.4.4" + } + }, "@material-ui/styles": { "version": "4.11.4", "resolved": "https://registry.npmjs.org/@material-ui/styles/-/styles-4.11.4.tgz", diff --git a/examples/eth-dm/package.json b/examples/eth-dm/package.json index 4851ff1e55..1c7cb88c86 100644 --- a/examples/eth-dm/package.json +++ b/examples/eth-dm/package.json @@ -5,6 +5,7 @@ "homepage": "/js-waku/eth-dm", "dependencies": { "@material-ui/core": "^4.11.4", + "@material-ui/icons": "^4.11.2", "@testing-library/jest-dom": "^5.11.4", "@testing-library/react": "^11.1.0", "@testing-library/user-event": "^12.1.10", diff --git a/examples/eth-dm/src/App.tsx b/examples/eth-dm/src/App.tsx index b1580a2859..07dcbbd0a9 100644 --- a/examples/eth-dm/src/App.tsx +++ b/examples/eth-dm/src/App.tsx @@ -9,16 +9,61 @@ import { createPublicKeyMessage, KeyPair } from './crypto'; import { encode, PublicKeyMessage } from './messages'; import Messages, { Message } from './Messages'; import 'fontsource-roboto'; -import { Button } from '@material-ui/core'; +import { + AppBar, + Button, + IconButton, + Toolbar, + Typography, +} from '@material-ui/core'; import SendMessage from './SendMessage'; import KeyPairHandling from './key_pair_handling/KeyPairHandling'; import InitWaku from './InitWaku'; +import { + createMuiTheme, + ThemeProvider, + makeStyles, +} from '@material-ui/core/styles'; +import { teal, purple, green } from '@material-ui/core/colors'; +import WifiIcon from '@material-ui/icons/Wifi'; export const PublicKeyContentTopic = '/eth-dm/1/public-key/json'; export const DirectMessageContentTopic = '/eth-dm/1/direct-message/json'; declare let window: any; +const theme = createMuiTheme({ + palette: { + primary: { + main: purple[500], + }, + secondary: { + main: teal[600], + }, + }, +}); + +const useStyles = makeStyles({ + root: { + textAlign: 'center', + display: 'flex', + flexDirection: 'column', + minHeight: '100vh', + }, + appBar: { + // height: '200p', + }, + container: { + display: 'flex', + flex: 1, + }, + main: { + flex: 1, + margin: '10px', + }, + wakuStatus: {}, +}); + function App() { const [waku, setWaku] = useState(); const [provider, setProvider] = useState(); @@ -28,6 +73,8 @@ function App() { const [messages, setMessages] = useState([]); const [address, setAddress] = useState(); + const classes = useStyles(); + useEffect(() => { if (provider) return; try { @@ -72,34 +119,58 @@ function App() { }; return ( -
    -
    - - setEthDmKeyPair(keyPair)} - /> -
    - + +
    + + + Ethereum Direct Message + + + + + + +
    +
    + +
    + Eth-DM Key Pair + setEthDmKeyPair(keyPair)} + /> + +
    +
    + Messaging + + +
    +
    - - -
    -
    +
  • + ); } diff --git a/examples/eth-dm/src/InitWaku.tsx b/examples/eth-dm/src/InitWaku.tsx index 87e7553598..31eb4a3477 100644 --- a/examples/eth-dm/src/InitWaku.tsx +++ b/examples/eth-dm/src/InitWaku.tsx @@ -79,7 +79,7 @@ export default function InitWaku({ }; }); - return

    {!!waku ? 'Waku is ready' : 'Waku is loading'}

    ; + return
    ; } async function initWaku(): Promise { diff --git a/examples/eth-dm/src/key_pair_handling/KeyPairHandling.tsx b/examples/eth-dm/src/key_pair_handling/KeyPairHandling.tsx index ea22c6a91c..a9db833941 100644 --- a/examples/eth-dm/src/key_pair_handling/KeyPairHandling.tsx +++ b/examples/eth-dm/src/key_pair_handling/KeyPairHandling.tsx @@ -1,8 +1,32 @@ import { Button } from '@material-ui/core'; import { LoadKeyPair } from './LoadKeyPair'; import { SaveKeyPair } from './SaveKeyPair'; -import React from 'react'; +import React, { useState } from 'react'; import { generateEthDmKeyPair, KeyPair } from '../crypto'; +import { makeStyles } from '@material-ui/core/styles'; +import PasswordInput from './PasswordInput'; + +const useStyles = makeStyles({ + root: { + textAlign: 'center', + display: 'flex', + alignItems: 'center', + flexDirection: 'column', + margin: '5px', + }, + generate: { margin: '5px' }, + storage: { + margin: '5px', + }, + loadSave: { + display: 'flex', + flexDirection: 'row', + margin: '5px', + }, + loadSaveButton: { + margin: '5px', + }, +}); export interface Props { ethDmKeyPair: KeyPair | undefined; @@ -13,6 +37,10 @@ export default function KeyPairHandling({ ethDmKeyPair, setEthDmKeyPair, }: Props) { + const classes = useStyles(); + + const [password, setPassword] = useState(); + const generateKeyPair = () => { if (ethDmKeyPair) return; @@ -26,43 +54,33 @@ export default function KeyPairHandling({ }; return ( -
    -
    + -
    -
    - setEthDmKeyPair(keyPair)} - disabled={!!ethDmKeyPair} + Generate Eth-DM Key Pair + +
    + setPassword(p)} /> -
    -
    - +
    +
    + setEthDmKeyPair(keyPair)} + disabled={!!ethDmKeyPair} + password={password} + /> +
    +
    + +
    +
    ); diff --git a/examples/eth-dm/src/key_pair_handling/LoadKeyPair.tsx b/examples/eth-dm/src/key_pair_handling/LoadKeyPair.tsx index 23b864d295..2a79cb37c0 100644 --- a/examples/eth-dm/src/key_pair_handling/LoadKeyPair.tsx +++ b/examples/eth-dm/src/key_pair_handling/LoadKeyPair.tsx @@ -1,20 +1,15 @@ -import { Button, TextField } from '@material-ui/core'; -import React, { ChangeEvent, useState } from 'react'; +import { Button } from '@material-ui/core'; +import React from 'react'; import { loadKeyPairFromStorage } from './key_pair_storage'; import { KeyPair } from '../crypto'; export interface Props { setEthDmKeyPair: (keyPair: KeyPair) => void; disabled: boolean; + password: string | undefined; } -export function LoadKeyPair({ disabled, setEthDmKeyPair }: Props) { - const [password, setPassword] = useState(); - - const handlePasswordChange = (event: ChangeEvent) => { - setPassword(event.target.value); - }; - +export function LoadKeyPair({ password, disabled, setEthDmKeyPair }: Props) { const loadKeyPair = () => { if (disabled) return; if (!password) return; @@ -26,29 +21,13 @@ export function LoadKeyPair({ disabled, setEthDmKeyPair }: Props) { }; return ( -
    - - -
    + Load Eth-DM Key Pair from storage + ); } diff --git a/examples/eth-dm/src/key_pair_handling/PasswordInput.tsx b/examples/eth-dm/src/key_pair_handling/PasswordInput.tsx new file mode 100644 index 0000000000..cc0e770555 --- /dev/null +++ b/examples/eth-dm/src/key_pair_handling/PasswordInput.tsx @@ -0,0 +1,24 @@ +import { TextField } from '@material-ui/core'; +import React, { ChangeEvent } from 'react'; + +interface Props { + password: string | undefined; + setPassword: (password: string) => void; +} + +export default function PasswordInput({ password, setPassword }: Props) { + const handlePasswordChange = (event: ChangeEvent) => { + setPassword(event.target.value); + }; + + return ( + + ); +} diff --git a/examples/eth-dm/src/key_pair_handling/SaveKeyPair.tsx b/examples/eth-dm/src/key_pair_handling/SaveKeyPair.tsx index 2d476a3a70..f8fef3399a 100644 --- a/examples/eth-dm/src/key_pair_handling/SaveKeyPair.tsx +++ b/examples/eth-dm/src/key_pair_handling/SaveKeyPair.tsx @@ -1,19 +1,14 @@ -import { Button, TextField } from '@material-ui/core'; -import React, { ChangeEvent, useState } from 'react'; +import { Button } from '@material-ui/core'; +import React from 'react'; import { KeyPair } from '../crypto'; import { saveKeyPairToStorage } from './key_pair_storage'; export interface Props { ethDmKeyPair: KeyPair | undefined; + password: string | undefined; } -export function SaveKeyPair({ ethDmKeyPair }: Props) { - const [password, setPassword] = useState(); - - const handlePasswordChange = (event: ChangeEvent) => { - setPassword(event.target.value); - }; - +export function SaveKeyPair({ password, ethDmKeyPair }: Props) { const saveKeyPair = () => { if (!ethDmKeyPair) return; if (!password) return; @@ -23,29 +18,13 @@ export function SaveKeyPair({ ethDmKeyPair }: Props) { }; return ( -
    - - -
    + Save Eth-DM Key Pair to storage + ); } From 5bc0eddd3a4cbd344ce1970423bc88fcd1577755 Mon Sep 17 00:00:00 2001 From: Franck Royer Date: Tue, 29 Jun 2021 15:46:07 +1000 Subject: [PATCH 15/20] Move messaging components to module --- examples/eth-dm/src/App.tsx | 58 +++-------------- examples/eth-dm/src/BroadcastPublicKey.tsx | 62 +++++++++++++++++++ examples/eth-dm/src/InitWaku.tsx | 8 ++- examples/eth-dm/src/crypto.ts | 2 +- .../eth-dm/src/{ => messaging}/Messages.tsx | 3 + .../src/{ => messaging}/SendMessage.tsx | 6 +- .../src/{messages.ts => messaging/wire.ts} | 0 7 files changed, 84 insertions(+), 55 deletions(-) create mode 100644 examples/eth-dm/src/BroadcastPublicKey.tsx rename examples/eth-dm/src/{ => messaging}/Messages.tsx (94%) rename examples/eth-dm/src/{ => messaging}/SendMessage.tsx (95%) rename examples/eth-dm/src/{messages.ts => messaging/wire.ts} (100%) diff --git a/examples/eth-dm/src/App.tsx b/examples/eth-dm/src/App.tsx index 07dcbbd0a9..674910afaf 100644 --- a/examples/eth-dm/src/App.tsx +++ b/examples/eth-dm/src/App.tsx @@ -2,21 +2,19 @@ import '@ethersproject/shims'; import React, { useEffect, useState } from 'react'; import './App.css'; -import { Waku, WakuMessage } from 'js-waku'; +import { Waku } from 'js-waku'; import { ethers } from 'ethers'; import { Web3Provider } from '@ethersproject/providers'; -import { createPublicKeyMessage, KeyPair } from './crypto'; -import { encode, PublicKeyMessage } from './messages'; -import Messages, { Message } from './Messages'; +import { KeyPair } from './crypto'; +import Messages, { Message } from './messaging/Messages'; import 'fontsource-roboto'; import { AppBar, - Button, IconButton, Toolbar, Typography, } from '@material-ui/core'; -import SendMessage from './SendMessage'; +import SendMessage from './messaging/SendMessage'; import KeyPairHandling from './key_pair_handling/KeyPairHandling'; import InitWaku from './InitWaku'; import { @@ -26,9 +24,7 @@ import { } from '@material-ui/core/styles'; import { teal, purple, green } from '@material-ui/core/colors'; import WifiIcon from '@material-ui/icons/Wifi'; - -export const PublicKeyContentTopic = '/eth-dm/1/public-key/json'; -export const DirectMessageContentTopic = '/eth-dm/1/direct-message/json'; +import BroadcastPublicKey from './BroadcastPublicKey'; declare let window: any; @@ -68,7 +64,6 @@ function App() { const [waku, setWaku] = useState(); const [provider, setProvider] = useState(); const [ethDmKeyPair, setEthDmKeyPair] = useState(); - const [publicKeyMsg, setPublicKeyMsg] = useState(); const [publicKeys, setPublicKeys] = useState>(new Map()); const [messages, setMessages] = useState([]); const [address, setAddress] = useState(); @@ -93,31 +88,6 @@ function App() { .then((address) => setAddress(address)); }); - const broadcastPublicKey = () => { - if (!ethDmKeyPair) return; - if (!provider) return; - if (!waku) return; - - if (publicKeyMsg) { - const wakuMsg = encodePublicKeyWakuMessage(publicKeyMsg); - waku.lightPush.push(wakuMsg).catch((e) => { - console.error('Failed to send Public Key Message', e); - }); - } else { - createPublicKeyMessage(provider.getSigner(), ethDmKeyPair.publicKey) - .then((msg) => { - setPublicKeyMsg(msg); - const wakuMsg = encodePublicKeyWakuMessage(msg); - waku.lightPush.push(wakuMsg).catch((e) => { - console.error('Failed to send Public Key Message', e); - }); - }) - .catch((e) => { - console.error('Failed to creat Eth-Dm Publication message', e); - }); - } - }; - return (
    @@ -153,14 +123,11 @@ function App() { ethDmKeyPair={ethDmKeyPair} setEthDmKeyPair={(keyPair) => setEthDmKeyPair(keyPair)} /> - +
    Messaging @@ -175,8 +142,3 @@ function App() { } export default App; - -function encodePublicKeyWakuMessage(ethDmMsg: PublicKeyMessage): WakuMessage { - const payload = encode(ethDmMsg); - return WakuMessage.fromBytes(payload, PublicKeyContentTopic); -} diff --git a/examples/eth-dm/src/BroadcastPublicKey.tsx b/examples/eth-dm/src/BroadcastPublicKey.tsx new file mode 100644 index 0000000000..d46fd32c9d --- /dev/null +++ b/examples/eth-dm/src/BroadcastPublicKey.tsx @@ -0,0 +1,62 @@ +import { Button } from '@material-ui/core'; +import React, { useState } from 'react'; +import { createPublicKeyMessage, KeyPair } from './crypto'; +import { encode, PublicKeyMessage } from './messaging/wire'; +import { WakuMessage, Waku } from 'js-waku'; +import { Signer } from '@ethersproject/abstract-signer'; +import { PublicKeyContentTopic } from './InitWaku'; + +interface Props { + ethDmKeyPair: KeyPair | undefined; + waku: Waku | undefined; + signer: Signer | undefined; +} + +export default function BroadcastPublicKey({ + signer, + ethDmKeyPair, + waku, +}: Props) { + const [publicKeyMsg, setPublicKeyMsg] = useState(); + + const broadcastPublicKey = () => { + if (!ethDmKeyPair) return; + if (!signer) return; + if (!waku) return; + + if (publicKeyMsg) { + const wakuMsg = encodePublicKeyWakuMessage(publicKeyMsg); + waku.lightPush.push(wakuMsg).catch((e) => { + console.error('Failed to send Public Key Message', e); + }); + } else { + createPublicKeyMessage(signer, ethDmKeyPair.publicKey) + .then((msg) => { + setPublicKeyMsg(msg); + const wakuMsg = encodePublicKeyWakuMessage(msg); + waku.lightPush.push(wakuMsg).catch((e) => { + console.error('Failed to send Public Key Message', e); + }); + }) + .catch((e) => { + console.error('Failed to creat Eth-Dm Publication message', e); + }); + } + }; + + return ( + + ); +} + +function encodePublicKeyWakuMessage(ethDmMsg: PublicKeyMessage): WakuMessage { + const payload = encode(ethDmMsg); + return WakuMessage.fromBytes(payload, PublicKeyContentTopic); +} diff --git a/examples/eth-dm/src/InitWaku.tsx b/examples/eth-dm/src/InitWaku.tsx index 31eb4a3477..e2beb50888 100644 --- a/examples/eth-dm/src/InitWaku.tsx +++ b/examples/eth-dm/src/InitWaku.tsx @@ -1,9 +1,11 @@ import { Dispatch, SetStateAction, useEffect } from 'react'; import { Environment, getStatusFleetNodes, Waku, WakuMessage } from 'js-waku'; -import { decode, DirectMessage, PublicKeyMessage } from './messages'; +import { decode, DirectMessage, PublicKeyMessage } from './messaging/wire'; import { decryptMessage, KeyPair, validatePublicKeyMessage } from './crypto'; -import { Message } from './Messages'; -import { DirectMessageContentTopic, PublicKeyContentTopic } from './App'; +import { Message } from './messaging/Messages'; + +export const PublicKeyContentTopic = '/eth-dm/1/public-key/json'; +export const DirectMessageContentTopic = '/eth-dm/1/direct-message/json'; interface Props { waku: Waku | undefined; diff --git a/examples/eth-dm/src/crypto.ts b/examples/eth-dm/src/crypto.ts index f0db5d213e..b357efcfef 100644 --- a/examples/eth-dm/src/crypto.ts +++ b/examples/eth-dm/src/crypto.ts @@ -3,7 +3,7 @@ import '@ethersproject/shims'; import * as EthCrypto from 'eth-crypto'; import { ethers } from 'ethers'; import { Signer } from '@ethersproject/abstract-signer'; -import { DirectMessage, PublicKeyMessage } from './messages'; +import { DirectMessage, PublicKeyMessage } from './messaging/wire'; export interface KeyPair { privateKey: string; diff --git a/examples/eth-dm/src/Messages.tsx b/examples/eth-dm/src/messaging/Messages.tsx similarity index 94% rename from examples/eth-dm/src/Messages.tsx rename to examples/eth-dm/src/messaging/Messages.tsx index 1d3384bc4d..0f9f530461 100644 --- a/examples/eth-dm/src/Messages.tsx +++ b/examples/eth-dm/src/messaging/Messages.tsx @@ -1,3 +1,6 @@ +/** + * Clear text message + */ export interface Message { text: string; timestamp: Date; diff --git a/examples/eth-dm/src/SendMessage.tsx b/examples/eth-dm/src/messaging/SendMessage.tsx similarity index 95% rename from examples/eth-dm/src/SendMessage.tsx rename to examples/eth-dm/src/messaging/SendMessage.tsx index d38cba8437..9057c21df9 100644 --- a/examples/eth-dm/src/SendMessage.tsx +++ b/examples/eth-dm/src/messaging/SendMessage.tsx @@ -8,9 +8,9 @@ import { } from '@material-ui/core'; import React, { ChangeEvent, useState, KeyboardEvent } from 'react'; import { Waku, WakuMessage } from 'js-waku'; -import { DirectMessage, encode } from './messages'; -import { DirectMessageContentTopic } from './App'; -import { encryptMessage } from './crypto'; +import { DirectMessage, encode } from './wire'; +import { encryptMessage } from '../crypto'; +import { DirectMessageContentTopic } from '../InitWaku'; const useStyles = makeStyles((theme) => ({ formControl: { diff --git a/examples/eth-dm/src/messages.ts b/examples/eth-dm/src/messaging/wire.ts similarity index 100% rename from examples/eth-dm/src/messages.ts rename to examples/eth-dm/src/messaging/wire.ts From a46a05a5ea515d231fe30e8afdbd2e112870b5bf Mon Sep 17 00:00:00 2001 From: Franck Royer Date: Tue, 29 Jun 2021 15:53:59 +1000 Subject: [PATCH 16/20] Move messaging components to single component --- examples/eth-dm/src/App.tsx | 18 ++++++------- examples/eth-dm/src/messaging/Messaging.tsx | 30 +++++++++++++++++++++ 2 files changed, 38 insertions(+), 10 deletions(-) create mode 100644 examples/eth-dm/src/messaging/Messaging.tsx diff --git a/examples/eth-dm/src/App.tsx b/examples/eth-dm/src/App.tsx index 674910afaf..b375f0cfbc 100644 --- a/examples/eth-dm/src/App.tsx +++ b/examples/eth-dm/src/App.tsx @@ -6,15 +6,9 @@ import { Waku } from 'js-waku'; import { ethers } from 'ethers'; import { Web3Provider } from '@ethersproject/providers'; import { KeyPair } from './crypto'; -import Messages, { Message } from './messaging/Messages'; +import { Message } from './messaging/Messages'; import 'fontsource-roboto'; -import { - AppBar, - IconButton, - Toolbar, - Typography, -} from '@material-ui/core'; -import SendMessage from './messaging/SendMessage'; +import { AppBar, IconButton, Toolbar, Typography } from '@material-ui/core'; import KeyPairHandling from './key_pair_handling/KeyPairHandling'; import InitWaku from './InitWaku'; import { @@ -25,6 +19,7 @@ import { import { teal, purple, green } from '@material-ui/core/colors'; import WifiIcon from '@material-ui/icons/Wifi'; import BroadcastPublicKey from './BroadcastPublicKey'; +import Messaging from './messaging/Messaging'; declare let window: any; @@ -131,8 +126,11 @@ function App() {
    Messaging - - +
    diff --git a/examples/eth-dm/src/messaging/Messaging.tsx b/examples/eth-dm/src/messaging/Messaging.tsx new file mode 100644 index 0000000000..60d07ff98b --- /dev/null +++ b/examples/eth-dm/src/messaging/Messaging.tsx @@ -0,0 +1,30 @@ +import Messages, { Message } from './Messages'; +import { Waku } from 'js-waku'; +import SendMessage from './SendMessage'; +import { makeStyles } from '@material-ui/core'; + +const useStyles = makeStyles({ + root: { + display: 'flex', + alignItems: 'left', + flexDirection: 'column', + margin: '5px', + }, +}); + +interface Props { + waku: Waku | undefined; + recipients: Map; + messages: Message[]; +} + +export default function Messaging({ waku, recipients, messages }: Props) { + const classes = useStyles(); + + return ( +
    + + +
    + ); +} From b1a1c82de3ec2dcb2ff36c99dea985012e06893f Mon Sep 17 00:00:00 2001 From: Franck Royer Date: Tue, 29 Jun 2021 16:14:27 +1000 Subject: [PATCH 17/20] Use material ui list --- examples/eth-dm/src/messaging/Messages.tsx | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/examples/eth-dm/src/messaging/Messages.tsx b/examples/eth-dm/src/messaging/Messages.tsx index 0f9f530461..2c3c1f54d8 100644 --- a/examples/eth-dm/src/messaging/Messages.tsx +++ b/examples/eth-dm/src/messaging/Messages.tsx @@ -1,3 +1,6 @@ +import React from 'react'; +import { List, ListItem, ListItemText } from '@material-ui/core'; + /** * Clear text message */ @@ -10,16 +13,20 @@ export interface Props { messages: Message[]; } -export default function Messages(props: Props) { - const messages = props.messages.map((msg) => { +export default function Messages({ messages }: Props) { + return {generate(messages)}; +} + +function generate(messages: Message[]) { + return messages.map((msg) => { + const text = `<${formatDisplayDate(msg.timestamp)}> ${msg.text}`; + return ( -
  • - {formatDisplayDate(msg.timestamp)} {msg.text} -
  • + + + ); }); - - return
      {messages}
    ; } function formatDisplayDate(timestamp: Date): string { From 4284be142ff328ee7ae5703d08ae4da9f15d63f6 Mon Sep 17 00:00:00 2001 From: Franck Royer Date: Tue, 29 Jun 2021 16:21:32 +1000 Subject: [PATCH 18/20] Fix spelling --- .cspell.json | 3 ++- examples/eth-dm/src/InitWaku.tsx | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.cspell.json b/.cspell.json index c77fadba33..e32f92c4da 100644 --- a/.cspell.json +++ b/.cspell.json @@ -72,7 +72,8 @@ "wakuv", "wakunode", "webfonts", - "websockets" + "websockets", + "wifi" ], "flagWords": [], "ignorePaths": [ diff --git a/examples/eth-dm/src/InitWaku.tsx b/examples/eth-dm/src/InitWaku.tsx index e2beb50888..2ba9ecef35 100644 --- a/examples/eth-dm/src/InitWaku.tsx +++ b/examples/eth-dm/src/InitWaku.tsx @@ -17,7 +17,7 @@ interface Props { } /** - * Does all the waku initialisation + * Does all the waku initialization */ export default function InitWaku({ waku, From 8c62105d95b8e1a67bf965e914465157fb0a5a52 Mon Sep 17 00:00:00 2001 From: Franck Royer Date: Tue, 29 Jun 2021 16:34:19 +1000 Subject: [PATCH 19/20] Replace empty div with react fragment --- examples/eth-dm/src/InitWaku.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/examples/eth-dm/src/InitWaku.tsx b/examples/eth-dm/src/InitWaku.tsx index 2ba9ecef35..ad1a56b649 100644 --- a/examples/eth-dm/src/InitWaku.tsx +++ b/examples/eth-dm/src/InitWaku.tsx @@ -81,7 +81,10 @@ export default function InitWaku({ }; }); - return
    ; + // Returns an empty fragment. + // Taking advantages of React's state management and useEffect() + // Not sure it is best practice but it works. + return <>; } async function initWaku(): Promise { From 90cdd5385c9951be8d6bae7702cd4edd912b1035 Mon Sep 17 00:00:00 2001 From: Franck Royer Date: Tue, 29 Jun 2021 16:34:26 +1000 Subject: [PATCH 20/20] Fix typo --- examples/eth-dm/src/BroadcastPublicKey.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/eth-dm/src/BroadcastPublicKey.tsx b/examples/eth-dm/src/BroadcastPublicKey.tsx index d46fd32c9d..f73e4cf9a4 100644 --- a/examples/eth-dm/src/BroadcastPublicKey.tsx +++ b/examples/eth-dm/src/BroadcastPublicKey.tsx @@ -39,7 +39,7 @@ export default function BroadcastPublicKey({ }); }) .catch((e) => { - console.error('Failed to creat Eth-Dm Publication message', e); + console.error('Failed to create public key message', e); }); } };