Document not found (404)
-This URL is invalid, sorry. Please use the navigation bar or search to continue.
- -diff --git a/.nojekyll b/.nojekyll deleted file mode 100644 index f173110..0000000 --- a/.nojekyll +++ /dev/null @@ -1 +0,0 @@ -This file makes sure that Github Pages doesn't process mdBook's output. diff --git a/404.html b/404.html deleted file mode 100644 index 05d63e9..0000000 --- a/404.html +++ /dev/null @@ -1,191 +0,0 @@ - - -
- - -This URL is invalid, sorry. Please use the navigation bar or search to continue.
- -Here is the list of the code examples and the features they demonstrate. -To run or studies the example, click on the repo links.
-Repo: min-react-js-chat.
-Demonstrates:
-Repo: store-reactjs-chat.
-Demonstrates:
-Repo: unpkg-js-store.
-Demonstrates:
-Repo: web-chat.
-Demonstrates:
-Repo: eth-pm.
-Demonstrates:
-Repo: eth-pm-wallet-encryption.
-Demonstrates:
-eth_signTypedData_v4
eth_decrypt
Web3 Wallet APIRepo: TheBojda/waku-uber.
-Article: Decentralized Uber: Here's How I Built It With Status.im, Waku, and Vue.js.
-Demonstrates:
-A content topic is used for content based filtering.
-It allows you to filter out the messages that your dApp processes, -both when receiving live messages (Relay) or retrieving historical messages (Store).
-The format for content topics is as follows:
-/{dapp-name}/{version}/{content-topic-name}/{encoding}
dapp-name
: The name of your dApp, it must be unique to avoid conflict with other dApps.version
: We usually start at 1
, useful when introducing breaking changes in your messages.content-topic-name
: The actual content topic name to use for filtering.
-If your dApp uses DappConnect for several features,
-you should use a content topic per feature.encoding
: The encoding format of the message, Protobuf is most often used: proto
.For example: Your dApp's name is SuperCrypto, -it enables users to receive notifications and send private messages. -You may want to use the following content topics:
-/supercrypto/1/notification/proto
/supercrypto/1/private-message/proto
You can learn more about Waku topics in the 23/WAKU2-TOPICS specs.
- -The Waku Message format provides an easy way to encrypt messages using symmetric or asymmetric encryption. -The encryption comes with several handy design requirements: -confidentiality, authenticity and integrity.
-You can find more details about Waku Message Payload Encryption in 26/WAKU-PAYLOAD.
-With Waku Message Version 1, the entire payload is encrypted.
-Which means that the only discriminating data available in clear text is the content topic and timestamp (if present). -Hence, if Alice expects to receive messages under a given content topic, she needs to try to decrypt all messages received on said content topic.
-This needs to be kept in mind for scalability and forward secrecy concerns:
-By using Waku Message Version 1, you will need to provide a way to your users to generate and store keys in a secure manner. -Storing, backing up and recovering key is out of the scope of this guide.
-If key recovery is important for your dApp, then check out -SubtleCrypto.wrapKey() which can be used to securely store or export private keys.
-An example to save and load a key pair in local storage, protected with a password, can be found in Eth-PM.
-Whether you should use symmetric or asymmetric encryption depends on your use case.
-Symmetric encryption is done using a single key to encrypt and decrypt.
-Which means that if Alice knows the symmetric key K
and uses it to encrypt a message,
-she can also use K
to decrypt any message encrypted with K
,
-even if she is not the sender.
Group chats is a possible use case for symmetric encryption:
-All participants can use an out-of-band method to agree on a K
.
-Participants can then use K
to encrypt and decrypt messages within the group chat.
-Participants MUST keep K
secret to ensure that no external party can decrypt the group chat messages.
Asymmetric encryption is done using a key pair: -the public key is used to encrypt messages, -the matching private key is used to decrypt messages.
-For Alice to encrypt a message for Bob, she needs to know Bob's Public Key K
.
-Bob can then use his private key k
to decrypt the message.
-As long as Bob keep his private key k
secret, then he, and only he, can decrypt messages encrypted with K
.
Private 1:1 messaging is a possible use case for asymmetric encryption: -When Alice sends an encrypted message for Bob, only Bob can decrypt it.
-To use symmetric encryption, you first need to generate a key.
-Use generateSymmetricKey
for secure key generation:
import { generateSymmetricKey } from 'js-waku';
-
-const symmetricKey = generateSymmetricKey();
-
-To encrypt a message with the previously generated key,
-pass the key in the symKey
property to WakuMessage.fromBytes
.
Same as Waku Messages version 0 (unencrypted),
-payload
is your message payload and contentTopic
is the content topic for your dApp.
-See Receive and Send Messages Using Waku Relay for details.
import { WakuMessage } from 'js-waku';
-
-const message = await WakuMessage.fromBytes(payload, contentTopic, {
- symKey: symmetricKey
-});
-
-The Waku Message can then be sent to the Waku network using Waku Relay or Waku Light Push:
-await waku.lightPush.push(message);
-
-To decrypt messages, -whether they are received over Waku Relay or using Waku Store, -add the symmetric key as a decryption key to your Waku instance.
-waku.addDecryptionKey(symmetricKey);
-
-Alternatively, you can pass the key when creating the instance:
-import { Waku } from 'js-waku';
-
-const waku = Waku.create({ decryptionKeys: [symmetricKey] });
-
-It will attempt to decrypt any message it receives using the key, for both symmetric and asymmetric encryption.
-You can call addDecryptionKey
several times if you are using multiple keys,
-symmetric key and asymmetric private keys can be used together.
Messages that are not successfully decrypted are dropped.
-To use asymmetric encryption, you first need to generate a private key and calculate the corresponding public key.
-Use generatePrivateKey
for secure key generation:
import { generatePrivateKey, getPublicKey } from 'js-waku';
-
-const privateKey = generatePrivateKey();
-const publicKey = getPublicKey(privateKey);
-
-The private key must be securely stored and remain private. -If leaked then other parties may be able to decrypt the user's messages.
-The public key is unique for a given private key and can always be recovered given the private key, -hence it is not needed to save it as long as as the private key can be recovered.
-The public key is used to encrypt messages;
-to do so, pass it in the encPublicKey
property to WakuMessage.fromBytes
.
Same as clear Waku Messages,
-payload
is your message payload and contentTopic
is the content topic for your dApp.
-See Receive and Send Messages Using Waku Relay for details.
import { WakuMessage } from 'js-waku';
-
-const message = await WakuMessage.fromBytes(payload, contentTopic, {
- encPublicKey: publicKey
-});
-
-The Waku Message can then be sent to the Waku network using Waku Relay or Waku Light Push:
-await waku.lightPush.push(message);
-
-The private key is needed to decrypt messages.
-To decrypt messages, -whether they are received over Waku Relay or using Waku Store, -add the private key as a decryption key to your Waku instance.
-waku.addDecryptionKey(privateKey);
-
-Alternatively, you can pass the key when creating the instance:
-import { Waku } from 'js-waku';
-
-const waku = Waku.create({ decryptionKeys: [privateKey] });
-
-It will attempt to decrypt any message it receives using the key, for both symmetric and asymmetric encryption.
-You can call addDecryptionKey
several times if you are using multiple keys,
-symmetric key and asymmetric private keys can be used together.
Messages that are not successfully decrypted are dropped.
-WakuMessage
instancesWhen creating a Waku Message using WakuMessage.fromBytes
with an encryption key (symmetric or asymmetric),
-the payload gets encrypted.
-Which means that wakuMessage.payload
returns an encrypted payload:
import { WakuMessage } from 'js-waku';
-
-const message = await WakuMessage.fromBytes(payload, contentTopic, {
- encPublicKey: publicKey
-});
-
-console.log(message.payload); // This is encrypted
-
-However, WakuMessage
instances returned by WakuRelay
or WakuStore
are always decrypted.
WakuRelay
and WakuStore
never return messages that are encrypted.
-If a message was not successfully decrypted, then it will be dropped from the results.
Which means that WakuMessage
instances returned by WakuRelay
and WakuStore
always have a clear payload (in regard to Waku Message version 1):
import { Waku } from 'js-waku';
-
-const waku = Waku.create({ decryptionKeys: [privateKey] });
-
-const messages = await waku.store.queryHistory([contentTopic]);
-
-if (messages && messages[0]) {
- console.log(messages[0].payload); // This payload is decrypted
-}
-
-waku.relay.addObserver((message) => {
- console.log(message.payload); // This payload is decrypted
-}, [contentTopic]);
-
-The Eth-PM Web App example demonstrates both the use of symmetric and asymmetric encryption.
-Asymmetric encryption is used for private messages so that only the intended recipient can read said messages.
-Symmetric encryption is used for the public key messages. -In this instance, the same key is used for all users: the Keccak-256 hash of the content topic (which results in 32 bytes array). -While this does not add functional value, it does demonstrate the usage of symmetric encryption in a web app.
-A live version of Eth-PM can be found at https://status-im.github.io/js-waku/eth-pm/.
-The specifications of the protocol it implements can be found at 20/TOY-ETH-PM.
- -Waku Light Push enables a client to receive a confirmation when sending a message.
-The Waku Relay protocol sends messages to connected peers but does not provide any information on whether said peers have received messages. -This can be an issue when facing potential connectivity issues. -For example, when the connection drops easily, or it is connected to a small number of relay peers.
-Waku Light Push allows a client to get a response from a remote peer when sending a message. -Note this only guarantees that the remote peer has received the message, -it cannot guarantee propagation to the network.
-It also means weaker privacy properties as the remote peer knows the client is the originator of the message. -Whereas with Waku Relay, a remote peer would not know whether the client created or forwarded the message.
-You can find Waku Light Push's specifications on Vac RFC.
-Before starting, you need to choose a Content Topic for your dApp. -Check out the how to choose a content topic guide to learn more about content topics.
-For this guide, we are using a single content topic: /light-push-guide/1/guide/proto
.
You can install js-waku using your favorite package manager:
-npm install js-waku
-
-In order to interact with the Waku network, you first need a Waku instance:
-import { Waku } from 'js-waku';
-
-const wakuNode = await Waku.create({ bootstrap: true });
-
-Passing the bootstrap
option will connect your node to predefined Waku nodes.
-If you want to bootstrap to your own nodes, you can pass an array of multiaddresses instead:
import { Waku } from 'js-waku';
-
-const waku = await Waku.create({
- bootstrap: [
- '/dns4/node-01.ac-cn-hongkong-c.wakuv2.test.statusim.net/tcp/443/wss/p2p/16Uiu2HAkvWiyFsgRhuJEb9JfjYxEkoHLgnUQmr1N5mKWnYjxYRVm',
- '/dns4/node-01.do-ams3.wakuv2.test.statusim.net/tcp/443/wss/p2p/16Uiu2HAmPLe7Mzm8TsYUubgCAW1aJoeFScxrLj8ppHFivPo97bUZ'
- ]
-});
-
-When using the bootstrap
option, it may take some time to connect to other peers.
-To ensure that you have a light push peer available to send messages to,
-use the following function:
await waku.waitForConnectedPeer();
-
-The returned Promise
will resolve once you are connected to a Waku peer.
You can now send a message using Waku Light Push. -By default, it sends the messages to a single randomly selected light push peer. -The peer is selected among the dApp's connected peers.
-If the dApp is not connected to any light push peer, an error is thrown.
-import {WakuMessage} from 'js-waku';
-
-const wakuMessage = await WakuMessage.fromUtf8String('Here is a message', `/light-push-guide/1/guide/proto`);
-
-const ack = await waku.lightPush.push(wakuMessage);
-if (!ack?.isSuccess) {
- // Message was not sent
-}
-
-
- It is easy to use DappConnect with ReactJS. -In this guide, we will demonstrate how your ReactJS dApp can use Waku Relay to send and receive messages.
-Before starting, you need to choose a Content Topic for your dApp.
-Check out the how to choose a content topic guide to learn more about content topics.
-For this guide, we are using a single content topic: /min-react-js-chat/1/chat/proto
.
Create a new React app:
-npx create-react-app min-react-js-chat
-cd min-react-js-chat
-
-Then, install js-waku:
-npm install js-waku
-
-Start the dev server and open the dApp in your browser:
-npm run start
-
-Note: We have noticed some issues with React bundling due to npm
pulling an old version of babel.
-If you are getting an error about the optional chaining (?.)
-character not being valid, try cleaning up and re-installing your dependencies:
rm -rf node_modules package-lock.json
-npm install
-
-In order to interact with the Waku network, you first need a Waku instance.
-Go to App.js
and modify the App
function:
import { Waku } from 'js-waku';
-import * as React from 'react';
-
-function App() {
- const [waku, setWaku] = React.useState(undefined);
- const [wakuStatus, setWakuStatus] = React.useState('None');
-
- // Start Waku
- React.useEffect(() => {
- // If Waku is already assigned, the job is done
- if (!!waku) return;
- // If Waku status not None, it means we are already starting Waku
- if (wakuStatus !== 'None') return;
-
- setWakuStatus('Starting');
-
- // Create Waku
- Waku.create({ bootstrap: true }).then((waku) => {
- // Once done, put it in the state
- setWaku(waku);
- // And update the status
- setWakuStatus('Started');
- });
- }, [waku, wakuStatus]);
-
- return (
- <div className='App'>
- <header className='App-header'>
- // Display the status on the web page
- <p>{wakuStatus}</p>
- </header>
- </div>
- );
-}
-
-When using the bootstrap
option, it may take some time to connect to other peers.
-To ensure that you have relay peers available to send and receive messages,
-use the Waku.waitForConnectedPeer()
async function:
React.useEffect(() => {
- if (!!waku) return;
- if (wakuStatus !== 'None') return;
-
- setWakuStatus('Starting');
-
- Waku.create({ bootstrap: true }).then((waku) => {
- setWaku(waku);
- setWakuStatus('Connecting');
- waku.waitForConnectedPeer().then(() => {
- setWakuStatus('Ready');
- });
- });
-}, [waku, wakuStatus]);
-
-To define the Protobuf message format, -use protons
-npm install protons
-
-Define SimpleChatMessage
with two fields: timestamp
and text
.
import protons from 'protons';
-
-const proto = protons(`
-message SimpleChatMessage {
- uint64 timestamp = 1;
- string text = 2;
-}
-`);
-
-Create a function that takes the Waku instance and a message to send:
-import { WakuMessage } from 'js-waku';
-
-const ContentTopic = `/min-react-js-chat/1/chat/proto`;
-
-function sendMessage(message, timestamp, waku) {
- const time = timestamp.getTime();
-
- // Encode to protobuf
- const payload = proto.SimpleChatMessage.encode({
- timestamp: time,
- text: message
- });
-
- // Wrap in a Waku Message
- return WakuMessage.fromBytes(payload, ContentTopic).then((wakuMessage) =>
- // Send over Waku Relay
- waku.relay.send(wakuMessage)
- );
-}
-
-Then, add a button to the App
function:
function App() {
- const [waku, setWaku] = React.useState(undefined);
- const [wakuStatus, setWakuStatus] = React.useState('None');
- // Using a counter just for the messages to be different
- const [sendCounter, setSendCounter] = React.useState(0);
-
- React.useEffect(() => {
- // ... creates Waku
- }, [waku, wakuStatus]);
-
- const sendMessageOnClick = () => {
- // Check Waku is started and connected first.
- if (wakuStatus !== 'Ready') return;
-
- sendMessage(`Here is message #${sendCounter}`, waku, new Date()).then(() =>
- console.log('Message sent')
- );
-
- // For demonstration purposes.
- setSendCounter(sendCounter + 1);
- };
-
- return (
- <div className="App">
- <header className="App-header">
- <p>{wakuStatus}</p>
- <button onClick={sendMessageOnClick} disabled={wakuStatus !== 'Ready'}> // Grey the button is Waku is not yet ready.
- Send Message
- </button>
- </header>
- </div>
- );
-}
-
-To process incoming messages, you need to register an observer on Waku Relay. -First, you need to define the observer function.
-You will need to remove the observer when the component unmount.
-Hence, you need the reference to the function to remain the same.
-For that, use React.useCallback
:
const processIncomingMessage = React.useCallback((wakuMessage) => {
- // Empty message?
- if (!wakuMessage.payload) return;
-
- // Decode the protobuf payload
- const { timestamp, text } = proto.SimpleChatMessage.decode(
- wakuMessage.payload
- );
- const time = new Date();
- time.setTime(timestamp);
-
- // For now, just log new messages on the console
- console.log(`message received at ${time.toString()}: ${text}`);
-}, []);
-
-Then, add this observer to Waku Relay. -Do not forget to delete the observer is the component is being unmounted:
-React.useEffect(() => {
- if (!waku) return;
-
- // Pass the content topic to only process messages related to your dApp
- waku.relay.addObserver(processIncomingMessage, [ContentTopic]);
-
- // `cleanUp` is called when the component is unmounted, see ReactJS doc.
- return function cleanUp() {
- waku.relay.deleteObserver(processIncomingMessage, [ContentTopic]);
- };
-}, [waku, wakuStatus, processIncomingMessage]);
-
-The Waku work is now done. -Your dApp is able to send and receive messages using Waku. -For the sake of completeness, let's display received messages on the page.
-First, add incoming messages to the state of the App
component:
function App() {
- //...
-
- const [messages, setMessages] = React.useState([]);
-
- const processIncomingMessage = React.useCallback((wakuMessage) => {
- if (!wakuMessage.payload) return;
-
- const { text, timestamp } = proto.SimpleChatMessage.decode(
- wakuMessage.payload
- );
-
- const time = new Date();
- time.setTime(timestamp);
- const message = { text, timestamp: time };
-
- setMessages((messages) => {
- return [message].concat(messages);
- });
- }, []);
-
- // ...
-}
-
-Then, render the messages:
-function App() {
- // ...
-
- return (
- <div className="App">
- <header className="App-header">
- <p>{wakuStatus}</p>
- <button onClick={sendMessageOnClick} disabled={wakuStatus !== 'Ready'}>
- Send Message
- </button>
- <ul>
- {messages.map((msg) => {
- return (
- <li>
- <p>
- {msg.timestamp.toString()}: {msg.text}
- </p>
- </li>
- );
- })}
- </ul>
- </header>
- </div>
- );
-}
-
-And Voilà! You should now be able to send and receive messages. -Try out by opening the app from different browsers.
-You can see the complete code in the Minimal ReactJS Chat App.
- -It is easy to use DappConnect with ReactJS. -In this guide, we will demonstrate how your ReactJS dApp can use Waku Store to retrieve messages.
-DApps running on a phone or in a browser are often offline: -The browser could be closed or mobile app in the background.
-Waku Relay is a gossip protocol. -As a user, it means that your peers forward you messages they just received. -If you cannot be reached by your peers, then messages are not relayed; -relay peers do not save messages for later.
-However, Waku Store peers do save messages they relay, -allowing you to retrieve them at a later time. -The Waku Store protocol is best-effort and does not guarantee data availability. -Waku Relay should still be preferred when online; -Waku Store can be used after resuming connectivity: -For example, when the dApp starts.
-In this guide, we'll review how you can use Waku Store to retrieve messages.
-Before starting, you need to choose a Content Topic for your dApp. -Check out the how to choose a content topic guide to learn more about content topics.
-Create a new React app:
-npx create-react-app my-app
-cd my-app
-
-Then, install js-waku:
-npm install js-waku
-
-Start the dev server and open the dApp in your browser:
-npm run start
-
-Note: We have noticed some issues with React bundling due to npm
pulling an old version of babel.
-If you are getting an error about the optional chaining (?.)
-character not being valid, try cleaning up and re-installing your dependencies:
rm -rf node_modules package-lock.json
-npm install
-
-In order to interact with the Waku network, you first need a Waku instance.
-Go to App.js
and modify the App
function:
import { Waku } from 'js-waku';
-import * as React from 'react';
-
-function App() {
- const [waku, setWaku] = React.useState(undefined);
- const [wakuStatus, setWakuStatus] = React.useState('None');
-
- // Start Waku
- React.useEffect(() => {
- // If Waku status not None, it means we are already starting Waku
- if (wakuStatus !== 'None') return;
-
- setWakuStatus('Starting');
-
- // Create Waku
- Waku.create({ bootstrap: true }).then((waku) => {
- // Once done, put it in the state
- setWaku(waku);
- // And update the status
- setWakuStatus('Connecting');
- });
- }, [waku, wakuStatus]);
-
- return (
- <div className='App'>
- <header className='App-header'>
- // Display the status on the web page
- <p>{wakuStatus}</p>
- </header>
- </div>
- );
-}
-
-When using the bootstrap
option, it may take some time to connect to other peers.
-To ensure that you have store peers available to retrieve messages from,
-use the Waku.waitForConnectedPeer()
async function:
React.useEffect(() => {
- if (!waku) return;
-
- if (wakuStatus === 'Connected') return;
-
- waku.waitForConnectedPeer().then(() => {
- setWakuStatus('Connected');
- });
-}, [waku, wakuStatus]);
-
-Waku v2 protocols use protobuf by default.
-Let's review how you can use protobuf to decode structured data.
-First, define a data structure. -For this guide, we will use a simple chat message that contains a timestamp, nick and text:
-{
- timestamp: Date;
- nick: string;
- text: string;
-}
-
-To encode and decode protobuf payloads, you can use the protons package.
-npm install protons
-
-Define the data structure with protons:
-import protons from 'protons';
-
-const proto = protons(`
-message ChatMessage {
- uint64 timestamp = 1;
- string nick = 2;
- bytes text = 3;
-}
-`);
-
-You can learn about protobuf message definitions here: -Protocol Buffers Language Guide.
-To decode the messages retrieved from a Waku Store node,
-you need to extract the protobuf payload and decode it using protons
.
function decodeMessage(wakuMessage) {
- if (!wakuMessage.payload) return;
-
- const { timestamp, nick, text } = proto.ChatMessage.decode(
- wakuMessage.payload
- );
-
- // All fields in protobuf are optional so be sure to check
- if (!timestamp || !text || !nick) return;
-
- const time = new Date();
- time.setTime(timestamp);
-
- const utf8Text = Buffer.from(text).toString('utf-8');
-
- return { text: utf8Text, timestamp: time, nick };
-}
-
-
-You now have all the building blocks to retrieve and decode messages for a store node.
-Note that Waku Store queries are paginated.
-The API provided by js-waku
automatically traverses all pages of the Waku Store response.
-By default, the most recent page is retrieved first but this can be changed with the pageDirection
option.
First, define a React state to save the messages:
-function App() {
- const [messages, setMessages] = React.useState([]);
- /// [..]
-}
-
-Then, define processMessages
to decode and then store messages in the React state.
-You will pass processMessages
as a callback
option to WakuStore.queryHistory
.
-processMessages
will be called each time a page is received from the Waku Store.
const processMessages = (retrievedMessages) => {
- const messages = retrievedMessages.map(decodeMessage).filter(Boolean);
-
- setMessages((currentMessages) => {
- return currentMessages.concat(messages.reverse());
- });
-};
-
-Finally, pass processMessage
in WakuStore.queryHistory
as the callback
value:
waku.store
- .queryHistory([ContentTopic], { callback: processMessages });
-
-All together, you should now have:
-const ContentTopic = '/toy-chat/2/huilong/proto';
-
-function App() {
- // [..]
- // Store messages in the state
- const [messages, setMessages] = React.useState([]);
-
- React.useEffect(() => {
- if (wakuStatus !== 'Connected') return;
-
- const processMessages = (retrievedMessages) => {
- const messages = retrievedMessages.map(decodeMessage).filter(Boolean);
-
- setMessages((currentMessages) => {
- return currentMessages.concat(messages.reverse());
- });
- };
-
- waku.store
- .queryHistory([ContentTopic], { callback: processMessages })
- .catch((e) => {
- console.log('Failed to retrieve messages', e);
- });
- }, [waku, wakuStatus]);
-
- return (
- <div className='App'>
- <header className='App-header'>
- <h2>{wakuStatus}</h2>
- <h3>Messages</h3>
- <ul>
- <Messages messages={messages} />
- </ul>
- </header>
- </div>
- );
-}
-
-
-Note that WakuStore.queryHistory
select an available store node for you.
-However, it can only select a connected node, which is why the bootstrapping is necessary.
-It will throw an error if no store node is available.
By default, Waku Store nodes store messages for 30 days. -Depending on your use case, you may not need to retrieve 30 days worth of messages.
-Waku Message defines an optional unencrypted timestamp
field.
-The timestamp is set by the sender.
-By default, js-waku sets the timestamp of outgoing message to the current time.
You can filter messages that include a timestamp within given bounds with the timeFilter
option.
Retrieve messages up to a week old:
-const startTime = new Date();
-// 7 days/week, 24 hours/day, 60min/hour, 60secs/min, 100ms/sec
-startTime.setTime(startTime.getTime() - 7 * 24 * 60 * 60 * 1000);
-
-waku.store
- .queryHistory([ContentTopic], {
- callback: processMessages,
- timeFilter: { startTime, endTime: new Date() }
- });
-
-You can see the complete code in the Minimal ReactJS Waku Store App.
- -Waku Relay is a gossip protocol that enables you to send and receive messages. -You can find Waku Relay's specifications on Vac RFC.
-Before starting, you need to choose a Content Topic for your dApp. -Check out the how to choose a content topic guide to learn more about content topics.
-For this guide, we are using a single content topic: /relay-guide/1/chat/proto
.
You can install js-waku using your favorite package manager:
-npm install js-waku
-
-In order to interact with the Waku network, you first need a Waku instance:
-import { Waku } from 'js-waku';
-
-const waku = await Waku.create({ bootstrap: true });
-
-Passing the bootstrap
option will connect your node to predefined Waku nodes.
-If you want to bootstrap to your own nodes, you can pass an array of multiaddresses instead:
import { Waku } from 'js-waku';
-
-const waku = await Waku.create({
- bootstrap: [
- '/dns4/node-01.ac-cn-hongkong-c.wakuv2.test.statusim.net/tcp/443/wss/p2p/16Uiu2HAkvWiyFsgRhuJEb9JfjYxEkoHLgnUQmr1N5mKWnYjxYRVm',
- '/dns4/node-01.do-ams3.wakuv2.test.statusim.net/tcp/443/wss/p2p/16Uiu2HAmPLe7Mzm8TsYUubgCAW1aJoeFScxrLj8ppHFivPo97bUZ'
- ]
-});
-
-When using the bootstrap
option, it may take some time to connect to other peers.
-To ensure that you have relay peers available to send and receive messages,
-use the following function:
await waku.waitForConnectedPeer();
-
-The returned Promise
will resolve once you are connected to a Waku Relay peer.
To receive messages for your app, -you need to register an observer on relay for your app's content topic:
-const processIncomingMessage = (wakuMessage) => {
- console.log(`Message Received: ${wakuMessage.payloadAsUtf8}`);
-};
-
-waku.relay.addObserver(processIncomingMessage, ['/relay-guide/1/chat/proto']);
-
-You are now ready to send messages. -Let's start by sending simple strings as messages.
-To send a message, you need to wrap the message in a WakuMessage
.
-When using a basic string payload, you can use the WakuMessage.fromUtf8String
helper:
import { WakuMessage } from 'js-waku';
-
-const wakuMessage = await WakuMessage.fromUtf8String('Here is a message', `/relay-guide/1/chat/proto`);
-
-Then, use the relay
module to send the message to our peers,
-the message will then be relayed to the rest of the network thanks to Waku Relay:
await waku.relay.send(wakuMessage);
-
-Sending strings as messages in unlikely to cover your dApps needs.
-Waku v2 protocols use protobuf by default.
-Let's review how you can use protobuf to include structured objects in Waku Messages.
-First, define a data structure. -For this guide, we will use a simple chat message that contains a timestamp and text:
-{
- timestamp: Date;
- text: string;
-}
-
-To encode and decode protobuf payloads, you can use the protons package.
-First, install protons:
-npm install protons
-
-Then define the simple chat message:
-import protons from 'protons';
-
-const proto = protons(`
-message SimpleChatMessage {
- uint64 timestamp = 1;
- string text = 2;
-}
-`);
-
-You can learn about protobuf message definitions here: -Protocol Buffers Language Guide.
-Instead of wrapping an utf-8 string in a Waku Message, -you are going to wrap a protobuf payload.
-First, encode the object:
-const payload = proto.SimpleChatMessage.encode({
- timestamp: Date.now(),
- text: 'Here is a message'
-});
-
-Then, wrap it in a Waku Message:
-const wakuMessage = await WakuMessage.fromBytes(payload, ContentTopic);
-
-Now, you can send the message over Waku Relay the same way than before:
-await waku.relay.send(wakuMessage);
-
-To decode the messages received over Waku Relay,
-you need to extract the protobuf payload and decode it using protons
.
const processIncomingMessage = (wakuMessage) => {
- // No need to attempt to decode a message if the payload is absent
- if (!wakuMessage.payload) return;
-
- const { timestamp, text } = proto.SimpleChatMessage.decode(
- wakuMessage.payload
- );
-
- console.log(`Message Received: ${text}, sent at ${timestamp.toString()}`);
-};
-
-Like before, add this callback as an observer to Waku Relay:
-waku.relay.addObserver(processIncomingMessage, ['/relay-guide/1/chat/proto']);
-
-That is it! Now, you know how to send and receive messages over Waku using the Waku Relay protocol.
-Feel free to check out other guides or examples.
-Here is the final code:
-import { getBootstrapNodes, Waku, WakuMessage } from 'js-waku';
-import protons from 'protons';
-
-const proto = protons(`
-message SimpleChatMessage {
- uint64 timestamp = 1;
- string text = 2;
-}
-`);
-
-const wakuNode = await Waku.create();
-
-const nodes = await getBootstrapNodes();
-await Promise.all(nodes.map((addr) => waku.dial(addr)));
-
-const processIncomingMessage = (wakuMessage) => {
- // No need to attempt to decode a message if the payload is absent
- if (!wakuMessage.payload) return;
-
- const { timestamp, text } = proto.SimpleChatMessage.decode(
- wakuMessage.payload
- );
-
- console.log(`Message Received: ${text}, sent at ${timestamp.toString()}`);
-};
-
-waku.relay.addObserver(processIncomingMessage, ['/relay-guide/1/chat/proto']);
-
-const payload = proto.SimpleChatMessage.encode({
- timestamp: Date.now(),
- text: 'Here is a message'
-});
-const wakuMessage = await WakuMessage.fromBytes(payload, ContentTopic);
-await waku.relay.send(wakuMessage);
-
-
- DApps running on a phone or in a browser are often offline: -The browser could be closed or mobile app in the background.
-Waku Relay is a gossip protocol. -As a user, it means that your peers forward you messages they just received. -If you cannot be reached by your peers, then messages are not relayed; -relay peers do not save messages for later.
-However, Waku Store peers do save messages they relay, -allowing you to retrieve them at a later time. -The Waku Store protocol is best-effort and does not guarantee data availability. -Waku Relay should still be preferred when online; -Waku Store can be used after resuming connectivity: -For example, when the dApp starts.
-In this guide, we'll review how you can use Waku Store to retrieve messages.
-Before starting, you need to choose a Content Topic for your dApp. -Check out the how to choose a content topic guide to learn more about content topics.
-For this guide, we are using a single content topic: /store-guide/1/news/proto
.
You can install js-waku using your favorite package manager:
-npm install js-waku
-
-In order to interact with the Waku network, you first need a Waku instance:
-import { Waku } from 'js-waku';
-
-const wakuNode = await Waku.create({ bootstrap: true });
-
-Passing the bootstrap
option will connect your node to predefined Waku nodes.
-If you want to bootstrap to your own nodes, you can pass an array of multiaddresses instead:
import { Waku } from 'js-waku';
-
-const wakuNode = await Waku.create({
- bootstrap: [
- '/dns4/node-01.ac-cn-hongkong-c.wakuv2.test.statusim.net/tcp/443/wss/p2p/16Uiu2HAkvWiyFsgRhuJEb9JfjYxEkoHLgnUQmr1N5mKWnYjxYRVm',
- '/dns4/node-01.do-ams3.wakuv2.test.statusim.net/tcp/443/wss/p2p/16Uiu2HAmPLe7Mzm8TsYUubgCAW1aJoeFScxrLj8ppHFivPo97bUZ'
- ]
-});
-
-When using the bootstrap
option, it may take some times to connect to other peers.
-To ensure that you have store peers available to retrieve historical messages from,
-use the following function:
await waku.waitForConnectedPeer();
-
-The returned Promise will resolve once you are connected to a Waku Store peer.
-Waku v2 protocols use protobuf by default.
-Let's review how you can use protobuf to send structured data.
-First, define a data structure. -For this guide, we will use a simple news article that contains a date of publication, title and body:
-{
- date: Date;
- title: string;
- body: string;
-}
-
-To encode and decode protobuf payloads, you can use the protons package.
-First, install protons:
-npm install protons
-
-Then specify the data structure:
-import protons from 'protons';
-
-const proto = protons(`
-message ArticleMessage {
- uint64 date = 1;
- string title = 2;
- string body = 3;
-}
-`);
-
-You can learn about protobuf message definitions here: -Protocol Buffers Language Guide.
-To decode the messages retrieved from a Waku Store node,
-you need to extract the protobuf payload and decode it using protons
.
const decodeWakuMessage = (wakuMessage) => {
- // No need to attempt to decode a message if the payload is absent
- if (!wakuMessage.payload) return;
-
- const { date, title, body } = proto.SimpleChatMessage.decode(
- wakuMessage.payload
- );
-
- // In protobuf, fields are optional so best to check
- if (!date || !title || !body) return;
-
- const publishDate = new Date();
- publishDate.setTime(date);
-
- return { publishDate, title, body };
-};
-
-You now have all the building blocks to retrieve and decode messages for a store node.
-Store node responses are paginated.
-The WakuStore.queryHistory
API automatically query all the pages in a sequential manner.
-To process messages as soon as they received (page by page), use the callback
option:
const ContentTopic = '/store-guide/1/news/proto';
-
-const callback = (retrievedMessages) => {
- const articles = retrievedMessages
- .map(decodeWakuMessage) // Decode messages
- .filter(Boolean); // Filter out undefined values
-
- console.log(`${articles.length} articles have been retrieved`);
-};
-
-waku.store
- .queryHistory([ContentTopic], { callback })
- .catch((e) => {
- // Catch any potential error
- console.log('Failed to retrieve messages from store', e);
- });
-
-Note that WakuStore.queryHistory
select an available store node for you.
-However, it can only select a connected node, which is why the bootstrapping is necessary.
-It will throw an error if no store node is available.
By default, Waku Store nodes store messages for 30 days. -Depending on your use case, you may not need to retrieve 30 days worth of messages.
-Waku Message defines an optional unencrypted timestamp
field.
-The timestamp is set by the sender.
-By default, js-waku sets the timestamp of outgoing message to the current time.
You can filter messages that include a timestamp within given bounds with the timeFilter
option.
Retrieve messages up to a week old:
-// [..] `ContentTopic` and `callback` definitions
-
-const startTime = new Date();
-// 7 days/week, 24 hours/day, 60min/hour, 60secs/min, 100ms/sec
-startTime.setTime(startTime.getTime() - 7 * 24 * 60 * 60 * 1000);
-
-waku.store
- .queryHistory([ContentTopic], {
- callback,
- timeFilter: { startTime, endTime: new Date() }
- })
- .catch((e) => {
- console.log('Failed to retrieve messages from store', e);
- });
-
-You can see a similar example implemented in ReactJS in the Minimal ReactJS Waku Store App.
- -DappConnect is a suite of libraries, SDKs and documentations to help you use Waku in your dApp.
-Waku is a decentralized, censorship-resistant, network and protocol family. -It enables you to add communication features to your dApp in a decentralized manner, -ensuring to your users that they will not be censored or de-platformed.
-Waku can be used for chat purposes and for many machine-to-machine use cases. -You can learn more about Waku at waku.vac.dev.
-JS-Waku is the TypeScript implementation of the Waku protocol, -built for browser environment.
-The quick start presents an easy way to send and receive messages using js-waku.
-The guides explain specific js-waku features -and how it can be used with popular web frameworks.
-The js-waku repository also holds a number of examples. -The examples are working Proof-of-Concepts that demonstrate how to use js-waku. -Check out the example list to see what usage each example demonstrates.
-Finally, if you want to learn how Waku works under the hoods, check the specs at rfc.vac.dev.
-If you encounter any bug or would like to propose new features, feel free to open an issue.
-To get help, join #dappconnect-support on Vac Discord or Telegram. -For more general discussion and latest news, join #dappconnect on Vac Discord or Telegram.
- -