mirror of
https://github.com/logos-messaging/docs.waku.org.git
synced 2026-08-25 11:31:16 +00:00
Split website in 4:
- build - run node - learn - research
This commit is contained in:
+190
@@ -0,0 +1,190 @@
|
||||
---
|
||||
title: Bootstrap Nodes and Discover Peers
|
||||
hide_table_of_contents: true
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
|
||||
This guide provides detailed steps to bootstrap your your node using [Static Peers](/learn/concepts/static-peers) and discover peers in the Waku Network using [DNS Discovery](/learn/concepts/dns-discovery).
|
||||
|
||||
:::info
|
||||
If you do not set up a bootstrap node or discovery mechanism, your node will not connect to any remote peer.
|
||||
:::
|
||||
|
||||
:::tip
|
||||
Until [node incentivisation](/learn/research#prevention-of-denial-of-service-dos-and-node-incentivisation) is in place, you should [operate extra nodes](/#run-a-waku-node) alongside the ones provided by the Waku Network. When running a node, we recommend using the [DNS Discovery and Static Peers](#configure-dns-discovery-and-static-peers) configuration to connect to both the Waku Network and your node.
|
||||
:::
|
||||
|
||||
## Default bootstrap method
|
||||
|
||||
The `@waku/sdk` package provides a built-in bootstrapping method that uses [DNS Discovery](/learn/concepts/dns-discovery) to locate peers from the `waku v2.prod` `ENR` tree.
|
||||
|
||||
```js
|
||||
import { createLightNode } from "@waku/sdk";
|
||||
|
||||
// Bootstrap node using the default bootstrap method
|
||||
const node = await createLightNode({ defaultBootstrap: true });
|
||||
```
|
||||
|
||||
## Configure static peers
|
||||
|
||||
To set [static peers](/learn/concepts/static-peers), a list of `multiaddr` to bootstrap the node should be passed to the `bootstrapPeers` parameter of the `createLightNode()` function:
|
||||
|
||||
```js
|
||||
import { createLightNode } from "@waku/sdk";
|
||||
|
||||
// Bootstrap node using static peers
|
||||
const node = await createLightNode({
|
||||
bootstrapPeers: ["[PEER MULTIADDR]"],
|
||||
});
|
||||
```
|
||||
|
||||
For example, consider a node that connects to two static peers on the same local host (IP: `0.0.0.0`) using TCP ports `60002` and `60003` with WebSocket enabled:
|
||||
|
||||
```js
|
||||
// Define the list of static peers to bootstrap
|
||||
const peers = [
|
||||
"/ip4/0.0.0.0/tcp/60002/ws/p2p/16Uiu2HAkzjwwgEAXfeGNMKFPSpc6vGBRqCdTLG5q3Gmk2v4pQw7H",
|
||||
"/ip4/0.0.0.0/tcp/60003/ws/p2p/16Uiu2HAmFBA7LGtwY5WVVikdmXVo3cKLqkmvVtuDu63fe8safeQJ",
|
||||
];
|
||||
|
||||
// Bootstrap node using the static peers
|
||||
const node = await createLightNode({
|
||||
bootstrapPeers: peers,
|
||||
});
|
||||
```
|
||||
|
||||
Alternatively, you can dial a particular node like this:
|
||||
|
||||
```js
|
||||
// Define the list of static peers to bootstrap
|
||||
const peers = [
|
||||
"/ip4/0.0.0.0/tcp/60002/ws/p2p/16Uiu2HAkzjwwgEAXfeGNMKFPSpc6vGBRqCdTLG5q3Gmk2v4pQw7H",
|
||||
"/ip4/0.0.0.0/tcp/60003/ws/p2p/16Uiu2HAmFBA7LGtwY5WVVikdmXVo3cKLqkmvVtuDu63fe8safeQJ",
|
||||
];
|
||||
|
||||
const node = await createLightNode();
|
||||
|
||||
// In case nodes are using IP address and / or `ws` protocol - additional configuration is needed:
|
||||
/*
|
||||
const node = await createLightNode({
|
||||
libp2p: {
|
||||
filterMultiaddrs: false,
|
||||
},
|
||||
});
|
||||
*/
|
||||
|
||||
const promises = peers.map((multiaddr) => node.dial(multiaddr));
|
||||
|
||||
await Promise.all(promises);
|
||||
```
|
||||
|
||||
:::tip
|
||||
For local development using a `nwaku` node, use a `ws` address instead of `wss`. Remember that this setup is functional only when your web server is running locally. You can check how to get multi address of your locally run node in [Find node address](/guides/nwaku/find-node-address).
|
||||
:::
|
||||
|
||||
## Configure DNS discovery
|
||||
|
||||
To bootstrap a node using [DNS Discovery](/learn/concepts/dns-discovery), first install the `@waku/dns-discovery` package:
|
||||
|
||||
<Tabs groupId="package-manager">
|
||||
<TabItem value="npm" label="NPM">
|
||||
|
||||
```shell
|
||||
npm install @waku/dns-discovery
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="yarn" label="Yarn">
|
||||
|
||||
```shell
|
||||
yarn add @waku/dns-discovery
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
Then, use the `wakuDnsDiscovery()` function to provide a list of URLs for DNS node list in the format `enrtree://<key>@<fqdn>`:
|
||||
|
||||
```js
|
||||
import { createLightNode } from "@waku/sdk";
|
||||
import { wakuDnsDiscovery } from "@waku/dns-discovery";
|
||||
|
||||
// Define DNS node list
|
||||
const enrTree = "enrtree://[PUBLIC KEY]@[DOMAIN NAME]";
|
||||
|
||||
// Define node requirements
|
||||
const NODE_REQUIREMENTS = {
|
||||
store: 3,
|
||||
lightPush: 3,
|
||||
filter: 3,
|
||||
};
|
||||
|
||||
// Bootstrap node using DNS Discovery
|
||||
const node = await createLightNode({
|
||||
libp2p: {
|
||||
peerDiscovery: [wakuDnsDiscovery([enrTree], NODE_REQUIREMENTS)],
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
For example, consider a node that uses the `waku v2.prod` and `waku v2.test` `ENR` trees for `DNS Discovery`:
|
||||
|
||||
```js
|
||||
import { enrTree } from "@waku/dns-discovery";
|
||||
|
||||
// Bootstrap node using DNS Discovery
|
||||
const node = await createLightNode({
|
||||
libp2p: {
|
||||
peerDiscovery: [
|
||||
wakuDnsDiscovery([enrTree["PROD"], enrTree["TEST"]], NODE_REQUIREMENTS),
|
||||
],
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## Configure DNS discovery and static peers
|
||||
|
||||
You can also bootstrap your node using [DNS Discovery](/learn/concepts/dns-discovery) and [Static Peers](/learn/concepts/static-peers) simultaneously:
|
||||
|
||||
```js
|
||||
import { createLightNode } from "@waku/sdk";
|
||||
import { bootstrap } from "@libp2p/bootstrap";
|
||||
import { enrTree, wakuDnsDiscovery } from "@waku/dns-discovery";
|
||||
|
||||
// Define the list of static peers to bootstrap
|
||||
const peers = [
|
||||
"/ip4/0.0.0.0/tcp/60002/ws/p2p/16Uiu2HAkzjwwgEAXfeGNMKFPSpc6vGBRqCdTLG5q3Gmk2v4pQw7H",
|
||||
"/ip4/0.0.0.0/tcp/60003/ws/p2p/16Uiu2HAmFBA7LGtwY5WVVikdmXVo3cKLqkmvVtuDu63fe8safeQJ",
|
||||
];
|
||||
|
||||
// Define node requirements
|
||||
const NODE_REQUIREMENTS = {
|
||||
store: 3,
|
||||
lightPush: 3,
|
||||
filter: 3,
|
||||
};
|
||||
|
||||
// Bootstrap node using DNS Discovery and static peers
|
||||
const node = await createLightNode({
|
||||
libp2p: {
|
||||
bootstrapPeers: peers,
|
||||
peerDiscovery: [wakuDnsDiscovery([enrTree["PROD"]], NODE_REQUIREMENTS)],
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## Retrieving connected peers
|
||||
|
||||
You can retrieve the array of peers connected to a node using the `libp2p.getPeers()` function within the `@waku/sdk` package:
|
||||
|
||||
```js
|
||||
import { createLightNode } from "@waku/sdk";
|
||||
|
||||
const node = await createLightNode({ defaultBootstrap: true });
|
||||
await node.waitForPeers();
|
||||
|
||||
// Retrieve array of peers connected to the node
|
||||
console.log(node.libp2p.getPeers());
|
||||
```
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
---
|
||||
title: Debug Your Waku DApp and WebSocket
|
||||
hide_table_of_contents: true
|
||||
---
|
||||
|
||||
This guide provides detailed steps to enable and use debug logs to troubleshoot your Waku DApp, whether in a NodeJS or browser environment and check your WebSocket connections in [nwaku](/guides/nwaku/run-node).
|
||||
|
||||
## Enabling debug logs
|
||||
|
||||
When resolving issues in your Waku DApp, debug logs can be helpful. The `@waku/sdk` and `libp2p` packages use the debug tool to handle and show logs that help you debug effectively.
|
||||
|
||||
### NodeJS environments
|
||||
|
||||
To enable debug logs for `@waku/sdk` on NodeJS, you must set the `DEBUG` environment variable. To only enable debug logs for `@waku/sdk`:
|
||||
|
||||
```shell
|
||||
export DEBUG=waku*
|
||||
```
|
||||
|
||||
To enable debug logs for both `@waku/sdk` and `libp2p`:
|
||||
|
||||
```shell
|
||||
export DEBUG=waku*,libp2p*
|
||||
```
|
||||
|
||||
To enable debug logs for all components:
|
||||
|
||||
```shell
|
||||
export DEBUG=*
|
||||
```
|
||||
|
||||
### Browser environments
|
||||
|
||||
To view debug logs in your browser's console, modify the local storage and add the `debug` key. Here are guides for various modern browsers:
|
||||
|
||||
- [Google Chrome](https://developer.chrome.com/docs/devtools/storage/localstorage/)
|
||||
- [Firefox](https://firefox-source-docs.mozilla.org/devtools-user/storage_inspector/local_storage_session_storage/index.html)
|
||||
- [JavaScript](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage)
|
||||
|
||||
| KEY | VALUE | DESCRIPTION |
|
||||
| - | - | - |
|
||||
| `debug` | `waku*` | Enables `@waku/sdk` debug logs |
|
||||
| `debug` | `waku*,libp2p*` | Enables `@waku/sdk` and `libp2p` debug logs |
|
||||
| `debug` | `*` | Enables all debug logs |
|
||||
|
||||
## Checking WebSocket setup
|
||||
|
||||
[Nwaku](/guides/nwaku/run-node) provides native support for WebSocket (`ws`) and WebSocket Secure (`wss`) protocols. These are the only [transports](/learn/concepts/transports) supported for connecting to the Waku Network via browsers.
|
||||
|
||||
It's important to note that browsers impose certain limitations on WebSocket usage:
|
||||
|
||||
- **Secure Context Requirement**: Insecure subroutines are prohibited in secure contexts. On an `https://` webpage, only `wss` connections are permitted, while `ws` connections are not allowed. This restriction does not apply if the webpage is served locally, like on `localhost` or `127.0.0.1`.
|
||||
- **Certificate Validation**: Certificate validation rules are consistent for `https` and `wss` connections. Certificates must not be expired, issued by a recognized Certificate Authority (CA), and match the domain name, among other criteria.
|
||||
- **User Feedback on Errors**: Web browsers do not display errors related to subroutines to the user. If a WebSocket connection encounters an issue, users won't be alerted directly; you'll need to check the browser's console for error details.
|
||||
|
||||
If you encounter difficulties when connecting to a remote node using `wss`, follow these steps:
|
||||
|
||||
### Try Websocat for connection
|
||||
|
||||
Attempt to connect using [websocat](https://github.com/vi/websocat), a tool for WebSocket interactions. Test the WebSocket port using the command:
|
||||
|
||||
```shell
|
||||
websocat -v wss://[WEBSOCKET HOST]:[WEBSOCKET PORT]
|
||||
```
|
||||
|
||||
For example, consider a `nwaku` node with the multiaddr as `/dns4/nwakunode.com/tcp/1234/wss/p2p/16...`:
|
||||
|
||||
```shell
|
||||
$ websocat -v wss://nwakunode.com:1234
|
||||
# ...
|
||||
[INFO websocat::ws_client_peer] Connected to ws
|
||||
```
|
||||
|
||||
The connection works if the `[INFO websocat::ws_client_peer] Connected to ws` log entry appears. If not, [check that the certificate is valid](#check-certificate-validity)
|
||||
|
||||
### Check certificate validity
|
||||
|
||||
Verify the certificate's validity by passing the `-k` or `--insecure` flag to handle invalid certificates in `websocat`:
|
||||
|
||||
```shell
|
||||
websocat -v -k wss://nwakunode.com:1234
|
||||
```
|
||||
|
||||
If this works, the certificate's invalidity is the problem, and you should investigate the cause of the error if not, [check if the WebSocket port is accessible](#check-websocket-port-accessibility).
|
||||
|
||||
### Check WebSocket port accessibility
|
||||
|
||||
Use `telnet` or another networking tool to verify if the WebSocket port is open and accessible. For example, if the multiaddr is `/dns4/nwakunode.com/tcp/1234/wss/p2p/16...`, use the command:
|
||||
|
||||
```shell
|
||||
$ telnet nwakunode.com 1234
|
||||
Trying 123.123.123.123...
|
||||
Connected to nwakunode.com.
|
||||
# ...
|
||||
```
|
||||
|
||||
If the connection succeeds, there might be an issue with `nwaku`. Consider seeking support on the [Waku Discord](https://discord.waku.org) or [raise an issue](https://github.com/waku-org/nwaku/issues/new). If the connection fails, ensure that the WebSocket port is open.
|
||||
Vendored
+55
@@ -0,0 +1,55 @@
|
||||
---
|
||||
title: JavaScript SDK FAQ
|
||||
hide_table_of_contents: true
|
||||
sidebar_label: Frequently Asked Questions
|
||||
---
|
||||
|
||||
import { AccordionItem } from '@site/src/components/mdx'
|
||||
|
||||
<AccordionItem title="How do I install the @waku/sdk package in my project?">
|
||||
You can add the JavaScript SDK to your project using NPM, Yarn, or a CDN. Check out the <a href="/guides/js-waku/#installation">installation guide</a> to get started.
|
||||
</AccordionItem>
|
||||
|
||||
<AccordionItem title="Why should I use Protocol Buffers for my application's message structure when using Waku?">
|
||||
Protocol Buffers ensure consistent formatting, interoperability, and backward compatibility for your application's messages, with a smaller payload size than JSON. Check out the <a href="/guides/js-waku/#message-structure">installation guide</a> and <a href="https://protobuf.dev/overview/">Protobuf documentation</a> to learn more.
|
||||
</AccordionItem>
|
||||
|
||||
<AccordionItem title="What are the steps to retrieve historical messages on Waku?">
|
||||
Check out the <a href="/guides/js-waku/store-retrieve-messages">Retrieve Messages Using Store Protocol</a> guide to learn how to retrieve and filter historical messages using the <a href="/learn/concepts/protocols#store">Store protocol</a>.
|
||||
</AccordionItem>
|
||||
|
||||
<AccordionItem title="How can I prevent Store peers from storing my messages?">
|
||||
When <a href="/guides/js-waku/light-send-receive#choose-a-content-topic">creating your message encoder</a>, you can configure the <strong>ephemeral</strong> option to prevent Store peers from keeping your messages on the Waku Network.
|
||||
</AccordionItem>
|
||||
|
||||
<AccordionItem title="How can I encrypt, decrypt, and sign messages in my Waku application?">
|
||||
You can encrypt and decrypt your messages using symmetric, ECIES, and noise encryption methods. Check out the <a href="/guides/js-waku/message-encryption">Encrypt, Decrypt, and Sign Your Messages</a> guide to get started.
|
||||
</AccordionItem>
|
||||
|
||||
<AccordionItem title="How do I integrate Waku into a React application?">
|
||||
Waku has a specialized SDK designed for building React applications. Check out the <a href="/guides/js-waku/use-waku-react">Build React DApps Using @waku/react</a> guide for instructions on installation and usage.
|
||||
</AccordionItem>
|
||||
|
||||
<AccordionItem title="How can I bootstrap and discover peers in the Waku Network for browser nodes?">
|
||||
The JavaScript SDK has a <a href="/guides/js-waku/configure-discovery#default-bootstrap-method">default bootstrap method</a> that can be configured with <a href="/learn/concepts/static-peers">Static Peers</a> and <a href="/learn/concepts/dns-discovery">DNS Discovery</a>. Check out the <a href="/guides/js-waku/configure-discovery">Bootstrap Nodes and Discover Peers</a> guide for setting up peer discovery for your node.
|
||||
</AccordionItem>
|
||||
|
||||
<AccordionItem title="How can I integrate Waku into a NodeJS application?">
|
||||
Though the JavaScript SDK isn't directly usable in NodeJS due to <a href="/guides/js-waku/run-waku-nodejs">certain limitations</a>, we recommend running <a href="/guides/nwaku/run-docker-compose">nwaku in a Docker container</a> and consuming its <a href="https://waku-org.github.io/waku-rest-api/">REST API</a> in a NodeJS application.
|
||||
</AccordionItem>
|
||||
|
||||
<AccordionItem title="How can I debug my Waku DApp and check WebSocket connections?">
|
||||
Check out the <a href="/guides/js-waku/debug-waku-dapp">Debug Your Waku DApp and WebSocket</a> guide to discover how to use debug logs to troubleshoot your Waku DApp and resolve connection issues with nwaku WebSockets.
|
||||
</AccordionItem>
|
||||
|
||||
<AccordionItem title="How can I manage unexpected disconnections of my Filter subscription from Waku?">
|
||||
We recommend regularly pinging peers to check for an active connection and reinitiating the subscription when it disconnects. Check out the <a href="/guides/js-waku/manage-filter">Manage Your Filter Subscriptions</a> guide for a detailed explanation and step-by-step instructions.
|
||||
</AccordionItem>
|
||||
|
||||
<AccordionItem title="How can I send images and videos on the Waku Network?">
|
||||
While it's possible to transmit media such as images as bytes on Waku, we recommend uploading your media to a CDN or a file system like <a href="https://ipfs.tech/">IPFS</a> and then sharing the corresponding URL via Waku.
|
||||
</AccordionItem>
|
||||
|
||||
<AccordionItem title="How can I connect to my own node?">
|
||||
To manually set your own node as a starting point use <a href="/guides/js-waku/configure-discovery#configure-static-peers">Configure static peers</a>.
|
||||
</AccordionItem>
|
||||
Vendored
+109
@@ -0,0 +1,109 @@
|
||||
---
|
||||
title: JavaScript Waku SDK
|
||||
hide_table_of_contents: true
|
||||
---
|
||||
|
||||
:::caution
|
||||
Currently, the JavaScript Waku SDK (`@waku/sdk`) is **NOT compatible** with React Native. We plan to add support for React Native in the future.
|
||||
:::
|
||||
|
||||
The [JavaScript Waku SDK](https://github.com/waku-org/js-waku) (`@waku/sdk`) provides a TypeScript implementation of the [Waku protocol](/) designed for web browser environments. Developers can seamlessly integrate Waku functionalities into web applications, enabling efficient communication and collaboration among users using the `@waku/sdk` package.
|
||||
|
||||
## Installation
|
||||
|
||||
Install the `@waku/sdk` package using your preferred package manager:
|
||||
|
||||
```mdx-code-block
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
```
|
||||
|
||||
<Tabs groupId="package-manager">
|
||||
<TabItem value="npm" label="NPM">
|
||||
|
||||
```shell
|
||||
npm install @waku/sdk
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="yarn" label="Yarn">
|
||||
|
||||
```shell
|
||||
yarn add @waku/sdk
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
You can also use the `@waku/sdk` package via a CDN without installing it on your system:
|
||||
|
||||
```js
|
||||
import * as waku from "https://unpkg.com/@waku/sdk@latest/bundle/index.js";
|
||||
```
|
||||
|
||||
## Message structure
|
||||
|
||||
We recommend creating a message structure for your application using [Protocol Buffers](https://protobuf.dev/) for the following reasons:
|
||||
|
||||
1. **Consistency:** Ensures uniform message format for easy parsing and processing.
|
||||
2. **Interoperability:** Facilitates effective communication between different parts of your application.
|
||||
3. **Compatibility:** Allows smooth communication between older and newer app versions.
|
||||
4. **Payload Size:** Minimizes payload overhead, especially for byte arrays, unlike JSON which adds significant overhead.
|
||||
|
||||
To get started, install the `protobufjs` package using your preferred package manager:
|
||||
|
||||
<Tabs groupId="package-manager">
|
||||
<TabItem value="npm" label="NPM">
|
||||
|
||||
```shell
|
||||
npm install protobufjs
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="yarn" label="Yarn">
|
||||
|
||||
```shell
|
||||
yarn add protobufjs
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
You can also use the `protobufjs` package via a CDN without installing it on your system:
|
||||
|
||||
```js
|
||||
// Import the CDN
|
||||
import "https://cdn.jsdelivr.net/npm/protobufjs@latest/dist/protobuf.min.js";
|
||||
```
|
||||
|
||||
```html
|
||||
<!-- Or include the protobufjs script -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/protobufjs@latest/dist/protobuf.min.js"></script>
|
||||
```
|
||||
|
||||
## Getting started
|
||||
|
||||
Have a look at the quick start guide and comprehensive tutorials to learn how to build applications using `@waku/sdk`:
|
||||
|
||||
| Guide | Description |
|
||||
|---------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| [Send and Receive Messages in a Reliable Channel](/guides/js-waku/reliable-channels) | Learn how to send and receive messages with a convenient SDK that provide various reliable functionalities out-of-the-box. |
|
||||
| [Send and Receive Messages Using Light Push and Filter](/guides/js-waku/light-send-receive) | Learn how to send and receive messages on light nodes using the [Light Push](/learn/concepts/protocols#light-push) and [Filter](/learn/concepts/protocols#filter) protocols |
|
||||
| [Retrieve Messages Using Store Protocol](/guides/js-waku/store-retrieve-messages) | Learn how to retrieve and filter historical messages on light nodes using the [Store protocol](/learn/concepts/protocols#store) |
|
||||
| [Encrypt, Decrypt, and Sign Your Messages](/guides/js-waku/message-encryption) | Learn how to use the [@waku/message-encryption](https://www.npmjs.com/package/@waku/message-encryption) package to encrypt, decrypt, and sign your messages |
|
||||
| [Build React DApps Using @waku/react](/guides/js-waku/use-waku-react) | Learn how to use the [@waku/react](https://www.npmjs.com/package/@waku/react) package seamlessly integrate `@waku/sdk` into a React application |
|
||||
| [Scaffold DApps Using @waku/create-app](/guides/js-waku/use-waku-create-app) | Learn how to use the [@waku/create-app](https://www.npmjs.com/package/@waku/create-app) package to bootstrap your next `@waku/sdk` project from various example templates |
|
||||
| [Bootstrap Nodes and Discover Peers](/guides/js-waku/configure-discovery) | Learn how to bootstrap your node using [Static Peers](/learn/concepts/static-peers) and discover peers using [DNS Discovery](/learn/concepts/dns-discovery) |
|
||||
| [Run @waku/sdk in a NodeJS Application](/guides/js-waku/run-waku-nodejs) | Learn our suggested approach for using the `@waku/sdk` package within a NodeJS application |
|
||||
| [Debug Your Waku DApp and WebSocket](/guides/js-waku/debug-waku-dapp) | Learn how to troubleshoot your Waku DApp using debug logs and check [WebSocket](/learn/concepts/transports) connections in [nwaku](/guides/nwaku/run-node) |
|
||||
| [Manage Your Filter Subscriptions](/guides/js-waku/manage-filter) | Learn how to manage [filter subscriptions](/learn/concepts/protocols#filter) and handle node disconnections in your application |
|
||||
|
||||
:::tip
|
||||
Until [node incentivisation](/learn/research#prevention-of-denial-of-service-dos-and-node-incentivisation) is in place, you should [operate extra nodes](/#run-a-waku-node) alongside the ones provided by the Waku Network. When running a node, we recommend using the [DNS Discovery and Static Peers](/guides/js-waku/configure-discovery#configure-dns-discovery-and-static-peers) configuration to connect to both the Waku Network and your node.
|
||||
:::
|
||||
|
||||
## Get help and report issues
|
||||
|
||||
To engage in general discussions, seek assistance, or stay updated with the latest news, visit the `#support` and `#js-waku-contribute` channels on the [Waku Discord](https://discord.waku.org).
|
||||
|
||||
If you discover bugs or want to suggest new features, do not hesitate to [open an issue](https://github.com/waku-org/js-waku/issues/new/) in the [js-waku repository](https://github.com/waku-org/js-waku). Your feedback and contributions are highly valued and will help improve the `@waku/sdk` package.
|
||||
+198
@@ -0,0 +1,198 @@
|
||||
---
|
||||
title: Send and Receive Messages Using Light Push and Filter
|
||||
hide_table_of_contents: true
|
||||
---
|
||||
|
||||
This guide provides detailed steps to start using the `@waku/sdk` package by setting up a [Light Node](/learn/glossary#light-node) to send messages using the [Light Push protocol](/learn/concepts/protocols#light-push), and receive messages using the [Filter protocol](/learn/concepts/protocols#filter). Have a look at the [installation guide](/guides/js-waku/#installation) for steps on adding `@waku/sdk` to your project.
|
||||
|
||||
## Create a light node
|
||||
|
||||
Use the `createLightNode()` function to create a [Light Node](/learn/glossary#light-node) and interact with the Waku Network:
|
||||
|
||||
```js
|
||||
import { createLightNode } from "@waku/sdk";
|
||||
|
||||
// Create and start a Light Node
|
||||
const node = await createLightNode({ defaultBootstrap: true });
|
||||
await node.start();
|
||||
|
||||
// Use the stop() function to stop a running node
|
||||
// await node.stop();
|
||||
```
|
||||
|
||||
:::info
|
||||
When the `defaultBootstrap` parameter is set to `true`, your node will be bootstrapped using the [default bootstrap method](/guides/js-waku/configure-discovery#default-bootstrap-method). Have a look at the [Bootstrap Nodes and Discover Peers](/guides/js-waku/configure-discovery) guide to learn more methods to bootstrap nodes.
|
||||
:::
|
||||
|
||||
A node needs to know how to route messages. By default, it will use The Waku Network configuration (`{ clusterId: 1, shards: [0,1,2,3,4,5,6,7] }`). For most applications, it's recommended to use autosharding:
|
||||
|
||||
```js
|
||||
// Create node with auto sharding (recommended)
|
||||
const node = await createLightNode({
|
||||
defaultBootstrap: true,
|
||||
networkConfig: {
|
||||
clusterId: 1,
|
||||
contentTopics: ["/my-app/1/notifications/proto"],
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### Alternative network configuration
|
||||
|
||||
If your project requires a specific network configuration, you can use static sharding:
|
||||
|
||||
```js
|
||||
// Create node with static sharding
|
||||
const node = await createLightNode({
|
||||
defaultBootstrap: true,
|
||||
networkConfig: {
|
||||
clusterId: 1,
|
||||
shards: [0, 1, 2, 3],
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## Connect to remote peers
|
||||
|
||||
Use the `node.waitForPeers()` function to wait for the node to connect with peers on the Waku Network:
|
||||
|
||||
```js
|
||||
// Wait for a successful peer connection
|
||||
await node.waitForPeers();
|
||||
```
|
||||
|
||||
The `protocols` parameter allows you to specify the [protocols](/learn/concepts/protocols) that the remote peers should have enabled:
|
||||
|
||||
```js
|
||||
import { Protocols } from "@waku/sdk";
|
||||
|
||||
// Wait for peer connections with specific protocols
|
||||
await node.waitForPeers([Protocols.LightPush, Protocols.Filter]);
|
||||
```
|
||||
|
||||
## Choose a content topic
|
||||
|
||||
Choose a [content topic](/learn/concepts/content-topics) for your application and create a message `encoder` and `decoder`:
|
||||
|
||||
```js
|
||||
import { createEncoder, createDecoder } from "@waku/sdk";
|
||||
|
||||
// Choose a content topic
|
||||
const contentTopic = "/light-guide/1/message/proto";
|
||||
|
||||
// Create a message encoder and decoder
|
||||
const encoder = createEncoder({ contentTopic });
|
||||
const decoder = createDecoder(contentTopic);
|
||||
```
|
||||
|
||||
The `ephemeral` parameter allows you to specify whether messages should **NOT** be stored by [Store peers](/guides/js-waku/store-retrieve-messages):
|
||||
|
||||
```js
|
||||
const encoder = createEncoder({
|
||||
contentTopic: contentTopic, // message content topic
|
||||
ephemeral: true, // allows messages NOT be stored on the network
|
||||
});
|
||||
```
|
||||
|
||||
The `pubsubTopicShardInfo` parameter allows you to configure a different network configuration for your `encoder` and `decoder`:
|
||||
|
||||
```js
|
||||
// Create the network config
|
||||
const networkConfig = { clusterId: 3, shards: [1, 2] };
|
||||
|
||||
// Create encoder and decoder with custom network config
|
||||
const encoder = createEncoder({
|
||||
contentTopic: contentTopic,
|
||||
pubsubTopicShardInfo: networkConfig,
|
||||
});
|
||||
const decoder = createDecoder(contentTopic, networkConfig);
|
||||
```
|
||||
|
||||
:::info
|
||||
In this example, users send and receive messages on a shared content topic. However, real applications may have users broadcasting messages while others listen or only have 1:1 exchanges. Waku supports all these use cases.
|
||||
:::
|
||||
|
||||
## Create a message structure
|
||||
|
||||
Create your application's message structure using [Protobuf's valid message](https://github.com/protobufjs/protobuf.js#usage) fields:
|
||||
|
||||
```js
|
||||
import protobuf from "protobufjs";
|
||||
|
||||
// Create a message structure using Protobuf
|
||||
const DataPacket = new protobuf.Type("DataPacket")
|
||||
.add(new protobuf.Field("timestamp", 1, "uint64"))
|
||||
.add(new protobuf.Field("sender", 2, "string"))
|
||||
.add(new protobuf.Field("message", 3, "string"));
|
||||
```
|
||||
|
||||
:::info
|
||||
Have a look at the [Protobuf installation](/guides/js-waku/#message-structure) guide for adding the `protobufjs` package to your project.
|
||||
:::
|
||||
|
||||
## Send messages using light push
|
||||
|
||||
To send messages over the Waku Network using the `Light Push` protocol, create a new message object and use the `lightPush.send()` function:
|
||||
|
||||
```js
|
||||
// Create a new message object
|
||||
const protoMessage = DataPacket.create({
|
||||
timestamp: Date.now(),
|
||||
sender: "Alice",
|
||||
message: "Hello, World!",
|
||||
});
|
||||
|
||||
// Serialise the message using Protobuf
|
||||
const serialisedMessage = DataPacket.encode(protoMessage).finish();
|
||||
|
||||
// Send the message using Light Push
|
||||
await node.lightPush.send(encoder, {
|
||||
payload: serialisedMessage,
|
||||
});
|
||||
```
|
||||
|
||||
## Receive messages using filter
|
||||
|
||||
To receive messages using the `Filter` protocol, create a callback function for message processing, then use the `filter.subscribe()` function to subscribe to a `content topic`:
|
||||
|
||||
```js
|
||||
// Create the callback function
|
||||
const callback = (wakuMessage) => {
|
||||
// Check if there is a payload on the message
|
||||
if (!wakuMessage.payload) return;
|
||||
// Render the messageObj as desired in your application
|
||||
const messageObj = DataPacket.decode(wakuMessage.payload);
|
||||
console.log(messageObj);
|
||||
};
|
||||
|
||||
// Create a Filter subscription
|
||||
const { error, subscription } = await node.filter.createSubscription({ contentTopics: [contentTopic] });
|
||||
|
||||
if (error) {
|
||||
// handle errors if happens
|
||||
throw Error(error);
|
||||
}
|
||||
|
||||
// Subscribe to content topics and process new messages
|
||||
await subscription.subscribe([decoder], callback);
|
||||
```
|
||||
|
||||
The `pubsubTopicShardInfo` parameter allows you to configure a different network configuration for your `Filter` subscription:
|
||||
|
||||
```js
|
||||
// Create the network config
|
||||
const networkConfig = { clusterId: 3, shards: [1, 2] };
|
||||
|
||||
// Create Filter subscription with custom network config
|
||||
const subscription = await node.filter.createSubscription(networkConfig);
|
||||
```
|
||||
|
||||
You can use the `subscription.unsubscribe()` function to stop receiving messages from a content topic:
|
||||
|
||||
```js
|
||||
await subscription.unsubscribe([contentTopic]);
|
||||
```
|
||||
|
||||
:::tip Congratulations!
|
||||
You have successfully sent and received messages over the Waku Network using the `Light Push` and `Filter` protocols. Have a look at the [light-js](https://github.com/waku-org/js-waku-examples/tree/master/examples/light-js) and [light-chat](https://github.com/waku-org/js-waku-examples/tree/master/examples/light-chat) examples for working demos.
|
||||
:::
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
---
|
||||
title: Manage Your Filter Subscriptions
|
||||
hide_table_of_contents: true
|
||||
---
|
||||
|
||||
This guide provides detailed steps to manage [Filter](/learn/concepts/protocols#filter) subscriptions and handle node disconnections in your application. Have a look at the [Send and Receive Messages Using Light Push and Filter](/guides/js-waku/light-send-receive) guide for using the `Light Push` and `Filter` protocols.
|
||||
|
||||
## Overview
|
||||
|
||||
Occasionally, your `Filter` subscriptions might disconnect from the Waku Network, resulting in messages not being received by your application. To manage your subscriptions, periodically ping peers to check for an active connection. The error message `"peer has no subscriptions"` indicates a failed ping due to disconnection. You can stop the pings if the disconnection/unsubscription is deliberate.
|
||||
|
||||
```mdx-code-block
|
||||
import FilterPingFlow from "@site/diagrams/_filter-ping-flow.md";
|
||||
|
||||
<FilterPingFlow />
|
||||
```
|
||||
|
||||
## Pinging filter subscriptions
|
||||
|
||||
The `@waku/sdk` package provides a `Filter.ping()` function to ping subscriptions and check for an active connection. To begin, create a `Filter` subscription:
|
||||
|
||||
```js
|
||||
// Create a Filter subscription
|
||||
const { error, subscription } = await node.filter.createSubscription({ contentTopics: [contentTopic] });
|
||||
|
||||
if (error) {
|
||||
// handle errors if happens
|
||||
throw Error(error);
|
||||
}
|
||||
|
||||
// Subscribe to content topics and process new messages
|
||||
await subscription.subscribe([decoder], callback);
|
||||
```
|
||||
|
||||
Next, create a function to ping and reinitiate the subscription:
|
||||
|
||||
```js
|
||||
const pingAndReinitiateSubscription = async () => {
|
||||
try {
|
||||
// Ping the subscription
|
||||
await subscription.ping();
|
||||
} catch (error) {
|
||||
if (
|
||||
// Check if the error message includes "peer has no subscriptions"
|
||||
error instanceof Error &&
|
||||
error.message.includes("peer has no subscriptions")
|
||||
) {
|
||||
// Reinitiate the subscription if the ping fails
|
||||
await subscription.subscribe([decoder], callback);
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Periodically ping the subscription
|
||||
await pingAndReinitiateSubscription();
|
||||
```
|
||||
|
||||
:::info
|
||||
Pings will fail when there are temporary network degradations or reachability issues. This does not mean that the underlying connection has been closed.
|
||||
:::
|
||||
|
||||
:::success Congratulations!
|
||||
You have successfully managed your `Filter` subscriptions to handle node disconnections in your application.
|
||||
:::
|
||||
+236
@@ -0,0 +1,236 @@
|
||||
---
|
||||
title: Encrypt, Decrypt, and Sign Your Messages
|
||||
hide_table_of_contents: true
|
||||
---
|
||||
|
||||
This guide provides detailed steps to use the [@waku/message-encryption](https://www.npmjs.com/package/@waku/message-encryption) package to encrypt, decrypt, and sign your messages using [Waku message payload encryption](/learn/glossary#waku-message-payload-encryption) methods.
|
||||
|
||||
:::info
|
||||
Waku uses libp2p noise encryption for node-to-node connections. However, no default encryption method is applied to the data sent over the network. This design choice enhances Waku's encryption flexibility, encouraging developers to freely use custom protocols or [Waku message payload encryption](/learn/glossary#waku-message-payload-encryption) methods.
|
||||
:::
|
||||
|
||||
## Installation
|
||||
|
||||
Install the required packages for integrating `@waku/message-encryption` using your preferred package manager:
|
||||
|
||||
```mdx-code-block
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
```
|
||||
|
||||
<Tabs groupId="package-manager">
|
||||
<TabItem value="npm" label="NPM">
|
||||
|
||||
```shell
|
||||
npm install @waku/message-encryption @waku/utils
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="yarn" label="Yarn">
|
||||
|
||||
```shell
|
||||
yarn add @waku/message-encryption @waku/utils
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Symmetric encryption
|
||||
|
||||
`Symmetric` encryption uses a single, shared key for message encryption and decryption. Use the `generateSymmetricKey()` function to generate a random symmetric key:
|
||||
|
||||
```js
|
||||
import { generateSymmetricKey } from "@waku/message-encryption";
|
||||
|
||||
// Generate a random symmetric key
|
||||
const symmetricKey = generateSymmetricKey();
|
||||
```
|
||||
|
||||
To send encrypted messages, create a `Symmetric` message `encoder` and send the message as usual:
|
||||
|
||||
```js title="Sender client"
|
||||
import { createEncoder } from "@waku/message-encryption/symmetric";
|
||||
|
||||
// Create a symmetric message encoder
|
||||
const encoder = createEncoder({
|
||||
contentTopic: contentTopic, // message content topic
|
||||
symKey: symmetricKey, // symmetric key for encrypting messages
|
||||
});
|
||||
|
||||
// Send the message using Light Push
|
||||
await node.lightPush.send(encoder, { payload });
|
||||
```
|
||||
|
||||
To decrypt the messages you receive, create a symmetric message `decoder` and process the messages as usual:
|
||||
|
||||
```js title="Receiver client"
|
||||
import { createDecoder } from "@waku/message-encryption/symmetric";
|
||||
|
||||
// Create a symmetric message decoder
|
||||
const decoder = createDecoder(contentTopic, symmetricKey);
|
||||
|
||||
// Receive messages from a Filter subscription
|
||||
await subscription.subscribe([decoder], callback);
|
||||
|
||||
// Retrieve messages from Store peers
|
||||
await node.store.queryWithOrderedCallback([decoder], callback);
|
||||
```
|
||||
|
||||
:::tip
|
||||
The symmetric key exchange between users can happen through an [out-of-band method](/learn/glossary#out-of-band). For example, where the key is embedded within the URL shared by a user to access a specific resource.
|
||||
:::
|
||||
|
||||
## ECIES encryption
|
||||
|
||||
`ECIES` encryption uses a public key for encryption and a private key for decryption. Use the `generatePrivateKey()` function to generate a random `ECDSA` private key:
|
||||
|
||||
```js
|
||||
import { generatePrivateKey, getPublicKey } from "@waku/message-encryption";
|
||||
|
||||
// Generate a random ECDSA private key, keep secure
|
||||
const privateKey = generatePrivateKey();
|
||||
|
||||
// Generate a public key from the private key, provide to the sender
|
||||
const publicKey = getPublicKey(privateKey);
|
||||
```
|
||||
|
||||
To send encrypted messages, create an `ECIES` message `encoder` with the public key and send the message as usual:
|
||||
|
||||
```js title="Sender client"
|
||||
import { createEncoder } from "@waku/message-encryption/ecies";
|
||||
|
||||
// Create an ECIES message encoder
|
||||
const encoder = createEncoder({
|
||||
contentTopic: contentTopic, // message content topic
|
||||
publicKey: publicKey, // ECIES public key for encrypting messages
|
||||
});
|
||||
|
||||
// Send the message using Light Push
|
||||
await node.lightPush.send(encoder, { payload });
|
||||
```
|
||||
|
||||
To decrypt the messages you receive, create an `ECIES` message `decoder` with the private key and process the messages as usual:
|
||||
|
||||
```js title="Receiver client"
|
||||
import { createDecoder } from "@waku/message-encryption/ecies";
|
||||
|
||||
// Create an ECIES message decoder
|
||||
const decoder = createDecoder(contentTopic, privateKey);
|
||||
|
||||
// Receive messages from a Filter subscription
|
||||
await subscription.subscribe([decoder], callback);
|
||||
|
||||
// Retrieve messages from Store peers
|
||||
await node.store.queryWithOrderedCallback([decoder], callback);
|
||||
```
|
||||
|
||||
:::tip
|
||||
Users can share their public key through broadcasting or [out-of-band methods](/learn/glossary#out-of-band), such as embedding it in a URL or sending an unencrypted message on another content topic for others to retrieve.
|
||||
:::
|
||||
|
||||
## Signing encrypted messages
|
||||
|
||||
Message signing helps in proving the authenticity of received messages. By attaching a signature to a message, you can verify its origin and integrity with absolute certainty.
|
||||
|
||||
:::info
|
||||
Signing messages is only possible when encrypted, but if your application does not require encryption, you can generate a symmetric key through hardcoded or deterministic methods using information available to all users.
|
||||
:::
|
||||
|
||||
The `sigPrivKey` parameter allows the `Symmetric` and `ECIES` message `encoders` to sign the message before encryption using an `ECDSA` private key:
|
||||
|
||||
```js title="Alice (sender) client"
|
||||
import { generatePrivateKey, getPublicKey } from "@waku/message-encryption";
|
||||
import { createEncoder as createSymmetricEncoder } from "@waku/message-encryption/symmetric";
|
||||
import { createEncoder as createECIESEncoder } from "@waku/message-encryption/ecies";
|
||||
|
||||
// Generate a random ECDSA private key for signing messages
|
||||
// ECIES encryption and message signing both use ECDSA keys
|
||||
// For this example, we'll call the sender of the message Alice
|
||||
const alicePrivateKey = generatePrivateKey();
|
||||
const alicePublicKey = getPublicKey(alicePrivateKey);
|
||||
|
||||
// Create a symmetric encoder that signs messages
|
||||
const symmetricEncoder = createSymmetricEncoder({
|
||||
contentTopic: contentTopic, // message content topic
|
||||
symKey: symmetricKey, // symmetric key for encrypting messages
|
||||
sigPrivKey: alicePrivateKey, // private key for signing messages before encryption
|
||||
});
|
||||
|
||||
// Create an ECIES encoder that signs messages
|
||||
const ECIESEncoder = createECIESEncoder({
|
||||
contentTopic: contentTopic, // message content topic
|
||||
publicKey: publicKey, // ECIES public key for encrypting messages
|
||||
sigPrivKey: alicePrivateKey, // private key for signing messages before encryption
|
||||
});
|
||||
|
||||
// Send and receive your messages as usual with Light Push and Filter
|
||||
await subscription.subscribe([symmetricEncoder], callback);
|
||||
await node.lightPush.send(symmetricEncoder, { payload });
|
||||
|
||||
await subscription.subscribe([ECIESEncoder], callback);
|
||||
await node.lightPush.send(ECIESEncoder, { payload });
|
||||
```
|
||||
|
||||
You can extract the `signature` and its public key (`signaturePublicKey`) from the [DecodedMessage](https://js.waku.org/classes/_waku_message_encryption.DecodedMessage.html) and compare it with the expected public key or use the `verifySignature()` function to verify the message origin:
|
||||
|
||||
```js title="Bob (receiver) client"
|
||||
import { generatePrivateKey } from "@waku/message-encryption";
|
||||
import { createEncoder } from "@waku/message-encryption/symmetric";
|
||||
|
||||
// Generate a random private key for signing messages
|
||||
// For this example, we'll call the receiver of the message Bob
|
||||
const bobPrivateKey = generatePrivateKey();
|
||||
|
||||
// Create an encoder that signs messages
|
||||
const encoder = createEncoder({
|
||||
contentTopic: contentTopic,
|
||||
symKey: symmetricKey,
|
||||
sigPrivKey: bobPrivateKey,
|
||||
});
|
||||
|
||||
// Modify the callback function to verify message signature
|
||||
const callback = (wakuMessage) => {
|
||||
// Extract the message signature and public key of the signature
|
||||
// You can compare the signaturePublicKey with Alice public key
|
||||
const signature = wakuMessage.signature;
|
||||
const signaturePublicKey = wakuMessage.signaturePublicKey;
|
||||
|
||||
// Verify the message was actually signed and sent by Alice
|
||||
// Alice's public key can be gotten from broadcasting or out-of-band methods
|
||||
if (wakuMessage.verifySignature(alicePublicKey)) {
|
||||
console.log("This message was signed by Alice");
|
||||
} else {
|
||||
console.log("This message was NOT signed by Alice");
|
||||
}
|
||||
};
|
||||
|
||||
await subscription.subscribe([encoder], callback);
|
||||
```
|
||||
|
||||
## Storing encryption keys
|
||||
|
||||
We used randomly generated keys for encryption and message signing in the provided examples, but real-world applications require consistent keys among client restarts. Have a look at the [Key Pair Handling](https://github.com/waku-org/js-waku-examples/tree/master/examples/eth-pm/src/key_pair_handling) example, which demonstrates the secure storage and retrieval of key information from local storage using [Subtle Crypto](https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto).
|
||||
|
||||
If you need a simple way to store your keys in hexadecimal format across your application, you can use the [@waku/utils](https://www.npmjs.com/package/@waku/utils) package:
|
||||
|
||||
```js
|
||||
import { bytesToHex, hexToBytes } from "@waku/utils/bytes";
|
||||
|
||||
// Generate random symmetric and private keys
|
||||
const symmetricKey = generateSymmetricKey();
|
||||
const privateKey = generatePrivateKey();
|
||||
|
||||
// Store the keys in hexadecimal format
|
||||
const symmetricKeyHex = bytesToHex(symmetricKey);
|
||||
const privateKeyHex = bytesToHex(privateKey);
|
||||
|
||||
// Restore the keys from hexadecimal format
|
||||
const restoredSymmetricKey = hexToBytes(symmetricKeyHex);
|
||||
const restoredPrivateKey = hexToBytes(privateKeyHex);
|
||||
```
|
||||
|
||||
:::tip Congratulations!
|
||||
You have successfully encrypted, decrypted, and signed your messages using `Symmetric` and `ECIES` encryption methods. Have a look at the [eth-pm](https://github.com/waku-org/js-waku-examples/tree/master/examples/eth-pm) example for a working demo.
|
||||
:::
|
||||
|
||||
<!-- [flush-notes](https://github.com/waku-org/js-waku-examples/tree/master/examples/flush-notes) and -->
|
||||
+197
@@ -0,0 +1,197 @@
|
||||
---
|
||||
title: Send and Receive Messages in a Reliable Channel
|
||||
hide_table_of_contents: true
|
||||
---
|
||||
|
||||
Learn how to send and receive messages with a convenient SDK that provide various reliable functionalities out-of-the-box.
|
||||
|
||||
:::warning
|
||||
This is an experimental feature and has a number of [limitations](https://github.com/waku-org/js-waku/pull/2526).
|
||||
:::
|
||||
|
||||
## Import Waku SDK
|
||||
|
||||
```shell
|
||||
npm install @waku/sdk@latest
|
||||
```
|
||||
|
||||
Or using a CDN, note this is an ESM package so `type="module"` is needed.
|
||||
|
||||
```html
|
||||
<script type="module">
|
||||
import {
|
||||
createLightNode,
|
||||
ReliableChannel
|
||||
} from 'https://unpkg.com/@waku/sdk@latest/bundle/index.js';
|
||||
|
||||
// Your code here
|
||||
|
||||
</script>
|
||||
```
|
||||
|
||||
## Create a Waku node
|
||||
|
||||
Use the `createLightNode()` function to create a [Light Node](/learn/glossary#light-node) and interact with the Waku Network:
|
||||
|
||||
```js
|
||||
import { createLightNode } from "@waku/sdk";
|
||||
|
||||
// Create a Light Node
|
||||
const node = await createLightNode({ defaultBootstrap: true });
|
||||
```
|
||||
|
||||
:::info
|
||||
When the `defaultBootstrap` parameter is set to `true`, your node will be bootstrapped using the [default bootstrap method](/guides/js-waku/configure-discovery#default-bootstrap-method). Have a look at the [Bootstrap Nodes and Discover Peers](/guides/js-waku/configure-discovery) guide to learn more methods to bootstrap nodes.
|
||||
:::
|
||||
|
||||
## Create encoders and decoders
|
||||
|
||||
Choose a [content topic](/learn/concepts/content-topics) for your application and create a message `encoder` and `decoder`:
|
||||
|
||||
```js
|
||||
import { createEncoder, createDecoder } from "@waku/sdk";
|
||||
|
||||
// Choose a content topic
|
||||
const ct = "/my-app/1/messages/proto";
|
||||
|
||||
// Create a message encoder and decoder
|
||||
const encoder = node.createEncoder({ contentTopic: ct });
|
||||
const decoder = node.createDecoder({ contentTopic: ct });
|
||||
```
|
||||
|
||||
You can also use [`@waku/message-encryption`](/guides/js-waku/message-encryption) to encrypt and decrypt messages using Waku libraries.
|
||||
|
||||
:::info
|
||||
In this example, users send and receive messages on a shared content topic. However, real applications may have users broadcasting messages while others listen or only have 1:1 exchanges. Waku supports all these use cases.
|
||||
:::
|
||||
|
||||
## Listen for connection status
|
||||
|
||||
The Waku node will emit `health` events to help you know whether the node is connected to the network.
|
||||
This can be useful to give feedback to the user, or stop some action (e.g. sending messages) when offline:
|
||||
|
||||
```js
|
||||
import { HealthStatus } from "@waku/sdk";
|
||||
|
||||
node.events.addEventListener("waku:health", (event) => {
|
||||
const health = event.detail;
|
||||
|
||||
if (health === HealthStatus.SufficientlyHealthy) {
|
||||
// Show to the user they are connected
|
||||
} else if (status === HealthStatus.MinimallyHealthy) {
|
||||
// Maybe put some clue to the user that while we are connected,
|
||||
// there may be issues sending or receiving messages
|
||||
} else {
|
||||
// Show to the user they are disconnected from the network
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## Create a reliable channel
|
||||
|
||||
You need to choose a channel name: it acts as an identifier to the conversation, participants will try to ensure they all have the same
|
||||
messages within a given channel.
|
||||
|
||||
```js
|
||||
const channelName = "channel-number-15"
|
||||
```
|
||||
|
||||
Finally, each participant need to identify themselves for reliability purposes, so they can confirm _others_ have received their messages.
|
||||
|
||||
It is up to you how to generate an id. Every participant **must** have a different id.
|
||||
|
||||
```js
|
||||
const senderId = generateRandomStringId();
|
||||
```
|
||||
|
||||
You can now create a reliable channel:
|
||||
|
||||
```js
|
||||
import { ReliableChannel } from "@waku/sdk";
|
||||
|
||||
const reliableChannel = await ReliableChannel.create(node, channelName, senderId, encoder, decoder)
|
||||
```
|
||||
|
||||
The channel will automatically start the Waku node and fetch messages.
|
||||
|
||||
## Create a message structure
|
||||
|
||||
Create your application's message structure using [Protobufjs](https://github.com/protobufjs/protobuf.js#usage):
|
||||
|
||||
```js
|
||||
import protobuf from "protobufjs";
|
||||
|
||||
// Create a message structure using Protobuf
|
||||
const DataPacket = new protobuf.Type("DataPacket")
|
||||
.add(new protobuf.Field("timestamp", 1, "uint64"))
|
||||
.add(new protobuf.Field("sender", 2, "string"))
|
||||
.add(new protobuf.Field("message", 3, "string"));
|
||||
```
|
||||
|
||||
:::info
|
||||
Have a look at the [Protobuf installation](/guides/js-waku/#message-structure) guide for adding the `protobufjs` package to your project.
|
||||
:::
|
||||
|
||||
## Listen to incoming messages
|
||||
|
||||
The reliable channel will emit incoming messages. To process them, simply add a listener:
|
||||
|
||||
```js
|
||||
reliableChannel.addEventListener("message-received", (event) => {
|
||||
const wakuMessage = event.detail;
|
||||
|
||||
// decode your payload using the protobuf object previously created
|
||||
const { timestamp, sender, message } = DataPacket.decode(wakuMessage.payload);
|
||||
|
||||
// ... process the message as you wish
|
||||
})
|
||||
```
|
||||
|
||||
## Send messages
|
||||
|
||||
To send messages in the reliable channel, encode the message in a payload.
|
||||
|
||||
```js
|
||||
// Create a new message object
|
||||
const protoMessage = DataPacket.create({
|
||||
timestamp: Date.now(),
|
||||
sender: "Alice",
|
||||
message: "Hello, World!",
|
||||
});
|
||||
|
||||
// Serialise the message using Protobuf
|
||||
const serialisedMessage = DataPacket.encode(protoMessage).finish();
|
||||
```
|
||||
|
||||
Then, send the message and setup listeners so you can know when the message:
|
||||
- has been sent
|
||||
- has been acknowledged by other participants in the channel
|
||||
- has encountered an error
|
||||
|
||||
```js
|
||||
// Send the message, and get the id to track events
|
||||
const messageId = reliableChannel.send(payload);
|
||||
|
||||
reliableChannel.addEventListener("sending-message-irrecoverable-error", (event) => {
|
||||
if (messageId === event.detail.messageId) {
|
||||
console.error('Failed to send message:', event.detail.error);
|
||||
// Show an error to the user
|
||||
}
|
||||
})
|
||||
|
||||
reliableChannel.addEventListener("message-sent", (event) => {
|
||||
if (messageId === event.detail) {
|
||||
// Message sent, show '✔' to the user, etc
|
||||
}
|
||||
})
|
||||
|
||||
reliableChannel.addEventListener("message-acknowledged", (event) => {
|
||||
if (messageId === event.detail) {
|
||||
// Message acknowledged by other participants, show '✔✔' to the user, etc
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
:::tip Congratulations!
|
||||
You have successfully sent and received messages over the Waku Network using our reliable protocols such as Scalable Data Sync (SDS) and P2P Reliability.
|
||||
:::
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
---
|
||||
title: "Run @waku/sdk in a NodeJS Application"
|
||||
hide_table_of_contents: true
|
||||
---
|
||||
|
||||
While the `@waku/sdk` package is primarily designed for browser environments, you can use it in a NodeJS application. However, there are certain limitations and considerations to keep in mind. This guide provides a comprehensive overview of using `@waku/sdk` in NodeJS.
|
||||
|
||||
## Limitations
|
||||
|
||||
### API compatibility
|
||||
|
||||
`@waku/sdk` prioritises browser compatibility, avoiding NodeJS APIs for simpler bundling. This design choice enhances browser API compatibility but sacrifices NodeJS optimisation. While many browser APIs work in NodeJS, they might need better optimisation.
|
||||
|
||||
### Protocol implementation
|
||||
|
||||
`@waku/sdk` focuses on the client side of the [Request/Response protocol](/learn/concepts/network-domains#requestresponse-domain). We'll have to replicate all the functionalities added to [nwaku](/guides/nwaku/run-node) to implement extra features.
|
||||
|
||||
### Codebase complexity
|
||||
|
||||
`@waku/sdk` aims to provide optimal default for the browser, striking a balance between browser and NodeJS compatibility while ensuring simplicity will add complexity.
|
||||
|
||||
### Browser-specific protocols
|
||||
|
||||
Certain features in `@waku/sdk` are tailored for browsers and might not translate seamlessly to NodeJS. For example, only `WebSocket` is supported in the browser, whereas a NodeJS application can benefit from using [transport methods](/learn/concepts/transports) like `TCP`.
|
||||
|
||||
`@waku/sdk` default peer management caters to the browser's ephemeral nature, which is different for NodeJS. This is why [DNS Discovery](/learn/concepts/dns-discovery) and [Peer Exchange](/learn/concepts/peer-exchange) are the default discovery mechanisms for the browser but not for NodeJS and desktop applications.
|
||||
|
||||
## Recommendations
|
||||
|
||||
Before using `@waku/sdk` in a NodeJS environment, take into account these limitations. For a more optimised solution, we recommend [running nwaku in a Docker container](/guides/nwaku/run-docker-compose) and consuming its [REST API](https://waku-org.github.io/waku-rest-api/).
|
||||
|
||||
## Future developments
|
||||
|
||||
There are plans to release a NodeJS package based on [nwaku](/guides/nwaku/run-node) to streamline the process of using Waku Network features in NodeJS applications. You can track the progress and updates here: [https://github.com/waku-org/nwaku/issues/1332](https://github.com/waku-org/nwaku/issues/1332).
|
||||
+205
@@ -0,0 +1,205 @@
|
||||
---
|
||||
title: Retrieve Messages Using Store Protocol
|
||||
hide_table_of_contents: true
|
||||
---
|
||||
|
||||
This guide provides detailed steps to create a Light Node for retrieving and filtering historical messages using the [Store protocol](/learn/concepts/protocols#store).
|
||||
|
||||
## Create a light node
|
||||
|
||||
Use the `createLightNode()` function to create a Light Node and interact with the Waku Network:
|
||||
|
||||
```js
|
||||
import { createLightNode } from "@waku/sdk";
|
||||
|
||||
// Create and start a Light Node
|
||||
const node = await createLightNode({ defaultBootstrap: true });
|
||||
await node.start();
|
||||
```
|
||||
|
||||
## Connect to store peers
|
||||
|
||||
Use the `node.waitForPeers()` method to wait for the node to connect with Store peers:
|
||||
|
||||
```js
|
||||
import { Protocols } from "@waku/sdk";
|
||||
|
||||
// Wait for a successful peer connection
|
||||
await node.waitForPeers([Protocols.Store]);
|
||||
```
|
||||
|
||||
You can also specify a dedicated Store peer to use for queries when creating the node. This is particularly useful when running your own Store node or when you want to use a specific Store node in the network:
|
||||
|
||||
```js
|
||||
const node = await createLightNode({
|
||||
defaultBootstrap: true,
|
||||
store: {
|
||||
peer: "/ip4/1.2.3.4/tcp/1234/p2p/16Uiu2HAm..." // multiaddr or PeerId of your Store node
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
If the specified Store peer is not available, the node will fall back to using random Store peers in the network.
|
||||
|
||||
## Choose a content topic
|
||||
|
||||
[Choose a content topic](/learn/concepts/content-topics) for filtering the messages to retrieve and create a message `decoder`:
|
||||
|
||||
```js
|
||||
import { createDecoder } from "@waku/sdk";
|
||||
|
||||
// Choose a content topic
|
||||
const contentTopic = "/store-guide/1/message/proto";
|
||||
|
||||
// Create a message decoder
|
||||
const decoder = createDecoder(contentTopic);
|
||||
```
|
||||
|
||||
## Retrieve messages
|
||||
|
||||
`@waku/sdk` provides the `queryWithOrderedCallback()` and `queryGenerator()` functions for querying `Store` nodes and retrieving historical or missed messages. The responses from `Store` nodes are paginated and require you to process each page sequentially.
|
||||
|
||||
### `queryWithOrderedCallback`
|
||||
|
||||
The `store.queryWithOrderedCallback()` function provides a straightforward method for querying `Store` nodes and processing messages in chronological order through a callback function. It accepts these parameters:
|
||||
|
||||
- `decoders`: List of `decoders` that specify the `content topic` to query for and their [message decryption](https://rfc.vac.dev/waku/standards/application/26/payload) methods.
|
||||
- `callback`: The callback function for processing the retrieved messages.
|
||||
- `options` (optional): [Query options](/guides/js-waku/store-retrieve-messages#store-query-options) to filter the retrieved messages.
|
||||
|
||||
```js
|
||||
// Create the callback function
|
||||
const callback = (wakuMessage) => {
|
||||
// Render the message/payload in your application
|
||||
console.log(wakuMessage);
|
||||
};
|
||||
|
||||
// Query the Store peer
|
||||
await node.store.queryWithOrderedCallback([decoder], callback);
|
||||
```
|
||||
|
||||
:::info
|
||||
The `queryWithOrderedCallback()` function always returns the most recent messages in a page first.
|
||||
:::
|
||||
|
||||
### `queryGenerator`
|
||||
|
||||
The `store.queryGenerator()` function provides more control and flexibility over processing messages retrieved from `Store` nodes through [Async Generators](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AsyncGenerator). It accepts these parameters:
|
||||
|
||||
- `decoders`: List of `decoders` that specify the `content topic` to query for and their [message decryption](https://rfc.vac.dev/waku/standards/application/26/payload) methods.
|
||||
- `options` (optional): [Query options](/guides/js-waku/store-retrieve-messages#store-query-options) to filter the retrieved messages.
|
||||
|
||||
```js
|
||||
// Create the store query
|
||||
const storeQuery = node.store.queryGenerator([decoder]);
|
||||
|
||||
// Process the messages
|
||||
for await (const messagesPromises of storeQuery) {
|
||||
// Fulfil the messages promises
|
||||
const messages = await Promise.all(
|
||||
messagesPromises.map(async (p) => {
|
||||
const msg = await p;
|
||||
// Render the message/payload in your application
|
||||
console.log(msg);
|
||||
})
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
:::info
|
||||
The `queryGenerator()` function always returns the oldest messages in a page first.
|
||||
:::
|
||||
|
||||
## Store query options
|
||||
|
||||
### `pageDirection`
|
||||
|
||||
The `pageDirection` option specifies the direction in which pages are retrieved:
|
||||
|
||||
- `BACKWARD` (default): Most recent page first.
|
||||
- `FORWARD`: Oldest page first.
|
||||
|
||||
```js
|
||||
import { PageDirection } from "@waku/sdk";
|
||||
|
||||
// Retrieve recent messages first
|
||||
const queryOptions = {
|
||||
pageDirection: PageDirection.BACKWARD,
|
||||
};
|
||||
|
||||
// Retrieve oldest messages first
|
||||
const queryOptions = {
|
||||
pageDirection: PageDirection.FORWARD,
|
||||
};
|
||||
|
||||
// Query the Store peer with options
|
||||
await node.store.queryWithOrderedCallback([decoder], callback, options);
|
||||
const storeQuery = node.store.queryGenerator([decoder, options]);
|
||||
```
|
||||
|
||||
### `cursor`
|
||||
|
||||
The `cursor` option specifies the starting index for retrieving messages. For example, consider a query that retrieves the first page messages and then continues with the next page:
|
||||
|
||||
```js
|
||||
import { waku } from "@waku/sdk";
|
||||
|
||||
// Create the callback function
|
||||
const messages = [];
|
||||
const callback = (wakuMessage) => {
|
||||
messages.push(wakuMessage);
|
||||
// Return "true" to stop retrieving pages
|
||||
// Here, it retrieves only the first page
|
||||
return true;
|
||||
};
|
||||
|
||||
// Retrieve the first page of messages
|
||||
// This retrieves all the messages if "return true" is not present
|
||||
await node.store.queryWithOrderedCallback([decoder], callback);
|
||||
|
||||
// Create the cursor
|
||||
const lastMessage = messages[messages.length - 1];
|
||||
const cursor = await waku.createCursor(lastMessage);
|
||||
|
||||
// Retrieve the next page of messages
|
||||
// The message at the cursor index is excluded from the result
|
||||
await node.store.queryWithOrderedCallback([decoder], callback, {
|
||||
cursor: cursor,
|
||||
});
|
||||
console.log(messages);
|
||||
```
|
||||
|
||||
:::info
|
||||
If you omit the `cursor` option, the query will start from the beginning or end of the history, depending on the [page direction](#pagedirection).
|
||||
:::
|
||||
|
||||
### `timeFilter`
|
||||
|
||||
The `timeFilter` option specifies a time frame to retrieve messages from. For example, consider a query that retrieves messages from the previous week:
|
||||
|
||||
```js
|
||||
// Get the time frame
|
||||
const endTime = new Date();
|
||||
const startTime = new Date();
|
||||
startTime.setDate(endTime.getDate() - 7);
|
||||
|
||||
// Retrieve a week of messages
|
||||
const queryOptions = {
|
||||
timeFilter: {
|
||||
startTime,
|
||||
endTime,
|
||||
},
|
||||
};
|
||||
|
||||
// Query the Store peer with options
|
||||
await node.store.queryWithOrderedCallback([decoder], callback, options);
|
||||
const storeQuery = node.store.queryGenerator([decoder, options]);
|
||||
```
|
||||
|
||||
:::info
|
||||
The `timeFilter` option significantly reduces message retrieval performance. To optimise it, consider resuming message retrieval using a [cursor](#cursor) that starts from the last seen message.
|
||||
:::
|
||||
|
||||
:::tip Congratulations!
|
||||
You have successfully retrieved and filtered historical messages on a Light Node using the `Store` protocol. Have a look at the [store-js](https://github.com/waku-org/js-waku-examples/tree/master/examples/store-js) and [store-reactjs-chat](https://github.com/waku-org/js-waku-examples/tree/master/examples/store-reactjs-chat) examples for working demos.
|
||||
:::
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
---
|
||||
title: "Scaffold DApps Using @waku/create-app"
|
||||
hide_table_of_contents: true
|
||||
---
|
||||
|
||||
This guide provides detailed steps to bootstrap your next `@waku/sdk` project from [various example templates](https://github.com/waku-org/js-waku-examples/tree/master/examples) using the [@waku/create-app](https://www.npmjs.com/package/@waku/create-app) package.
|
||||
|
||||
## Usage
|
||||
|
||||
Initialise a new `@waku/sdk` template using any of the following methods:
|
||||
|
||||
```mdx-code-block
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
```
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="npm" label="NPM">
|
||||
|
||||
```shell
|
||||
npx @waku/create-app [PROJECT DIRECTORY]
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="yarn" label="Yarn">
|
||||
|
||||
```shell
|
||||
yarn create @waku/app [PROJECT DIRECTORY]
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
Next, select a template to initialise your app from:
|
||||
|
||||

|
||||
|
||||
:::tip
|
||||
If you have previously installed `@waku/create-app` globally, we recommend uninstalling the package to ensure that `npx` always uses the latest version.
|
||||
:::
|
||||
|
||||
## Contributing new templates
|
||||
|
||||
We welcome and appreciate the contributions of templates for the `@waku/create-app` package. To contribute a template, please follow these steps:
|
||||
|
||||
1. Create the template, ensuring it is user-friendly and thoroughly tested.
|
||||
2. Place the template in the `examples` folder in the [js-waku-examples](https://github.com/waku-org/js-waku-examples) repository's root.
|
||||
3. Commit your changes with a detailed message and push them to your forked repository.
|
||||
4. Finally, submit a pull request to the [js-waku-examples](https://github.com/waku-org/js-waku-examples) repository.
|
||||
5. Our team will carefully review and merge your submission upon approval.
|
||||
|
||||
Waku also provides bounties to encourage community members to contribute to the network and earn rewards. To participate in the bounty program, head to [https://github.com/waku-org/bounties](https://github.com/waku-org/bounties).
|
||||
+311
@@ -0,0 +1,311 @@
|
||||
---
|
||||
title: "Build React DApps Using @waku/react"
|
||||
hide_table_of_contents: true
|
||||
---
|
||||
|
||||
:::caution
|
||||
Currently, the JavaScript Waku SDK (`@waku/sdk`) is **NOT compatible** with React Native. We plan to add support for React Native in the future.
|
||||
:::
|
||||
|
||||
The [@waku/react](https://www.npmjs.com/package/@waku/react) package provides components and UI adapters to integrate `@waku/sdk` into React applications effortlessly. This guide provides detailed steps for using `@waku/react` in your project.
|
||||
|
||||
## Install the dependencies
|
||||
|
||||
First, set up a project using any [production-grade React framework](https://react.dev/learn/start-a-new-react-project) or an existing React application. For this guide, we will create a boilerplate using [ViteJS](https://vitejs.dev/guide/):
|
||||
|
||||
```mdx-code-block
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
```
|
||||
|
||||
<Tabs groupId="package-manager">
|
||||
<TabItem value="npm" label="NPM">
|
||||
|
||||
```shell
|
||||
npm create vite@latest [PROJECT DIRECTORY] -- --template react
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="yarn" label="Yarn">
|
||||
|
||||
```shell
|
||||
yarn create vite [PROJECT DIRECTORY] --template react
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
Next, install the required packages for integrating `@waku/sdk` using your preferred package manager:
|
||||
|
||||
<Tabs groupId="package-manager">
|
||||
<TabItem value="npm" label="NPM">
|
||||
|
||||
```shell
|
||||
npm install @waku/react @waku/sdk protobufjs
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="yarn" label="Yarn">
|
||||
|
||||
```shell
|
||||
yarn add @waku/react @waku/sdk protobufjs
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Initialise the Waku provider
|
||||
|
||||
In the `main.jsx` file, which serves as the entry point for a React app, we will set up the `LightNodeProvider` [context provider](https://react.dev/reference/react/createContext#provider) to wrap the entire application within the Waku provider. Import the following on top of your file:
|
||||
|
||||
```js title="src/main.jsx"
|
||||
import { LightNodeProvider } from "@waku/react";
|
||||
|
||||
// Set the Light Node options
|
||||
const NODE_OPTIONS = { defaultBootstrap: true };
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root')).render(
|
||||
// Use the Light Node context provider
|
||||
<React.StrictMode>
|
||||
<LightNodeProvider options={NODE_OPTIONS}>
|
||||
<App />
|
||||
</LightNodeProvider>
|
||||
</React.StrictMode>,
|
||||
)
|
||||
```
|
||||
|
||||
Next, create and start a [Light Node](/learn/glossary#light-node) using the `useWaku()` function within the `App.jsx` file:
|
||||
|
||||
```js title="src/App.jsx"
|
||||
import { useWaku } from "@waku/react";
|
||||
|
||||
function App() {
|
||||
// Create and start a Light Node
|
||||
const { node, error, isLoading } = useWaku();
|
||||
|
||||
// "node" is the created Light Node
|
||||
// "error" captures any error that occurs during node creation
|
||||
// "isLoading" indicates whether the node is still being created
|
||||
}
|
||||
```
|
||||
|
||||
## Build the application interface
|
||||
|
||||
Let's build a user interface for sending messages and viewing past messages, modify the `App.jsx` file with the following code block:
|
||||
|
||||
```js title="src/App.jsx"
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useWaku } from "@waku/react";
|
||||
import { createEncoder, createDecoder } from "@waku/sdk";
|
||||
import protobuf from 'protobufjs';
|
||||
import './App.css'
|
||||
|
||||
function App() {
|
||||
const [inputMessage, setInputMessage] = useState("");
|
||||
const [messages, setMessages] = useState([]);
|
||||
|
||||
// Update the inputMessage state as the user input changes
|
||||
const handleInputChange = (e) => {
|
||||
setInputMessage(e.target.value);
|
||||
};
|
||||
|
||||
// Create and start a Light Node
|
||||
const { node, error, isLoading } = useWaku();
|
||||
|
||||
// Create a message encoder and decoder
|
||||
const contentTopic = "/waku-react-guide/1/chat/proto";
|
||||
const encoder = createEncoder({ contentTopic });
|
||||
const decoder = createDecoder(contentTopic);
|
||||
|
||||
// Create a message structure using Protobuf
|
||||
const DataPacket = new protobuf.Type("DataPacket")
|
||||
.add(new protobuf.Field("timestamp", 1, "uint64"))
|
||||
.add(new protobuf.Field("message", 2, "string"));
|
||||
|
||||
// Send the message using Light Push
|
||||
const sendMessage = async () => {}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="chat-interface">
|
||||
<h1>Waku React Demo</h1>
|
||||
<div className="chat-body">
|
||||
{messages.map((message, index) => (
|
||||
<div key={index} className="chat-message">
|
||||
<span>{new Date(message.timestamp).toUTCString()}</span>
|
||||
<div className="message-text">{message.message}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="chat-footer">
|
||||
<input
|
||||
type="text"
|
||||
id="message-input"
|
||||
value={inputMessage}
|
||||
onChange={handleInputChange}
|
||||
placeholder="Type your message..."
|
||||
/>
|
||||
<button className="send-button" onClick={sendMessage}>Send</button>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default App
|
||||
```
|
||||
|
||||
:::info
|
||||
In the code above, we also created a message `encoder` and `decoder` using the `createEncoder()` and `createDecoder()` functions, along with the application [message structure](/guides/js-waku/#message-structure) with Protobuf.
|
||||
:::
|
||||
|
||||
Next, modify the `App.css` file with the following code block:
|
||||
|
||||
```css title="src/App.css"
|
||||
#root {
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.chat-interface {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
.chat-body {
|
||||
flex-grow: 1;
|
||||
overflow-y: auto;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.message-text {
|
||||
background-color: #f1f1f1;
|
||||
color: #000;
|
||||
padding: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.chat-footer {
|
||||
display: flex;
|
||||
padding: 10px;
|
||||
background-color: #f1f1f1;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#message-input {
|
||||
flex-grow: 1;
|
||||
border-radius: 4px;
|
||||
padding: 10px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.send-button {
|
||||
background-color: #007bff;
|
||||
border-radius: 4px;
|
||||
}
|
||||
```
|
||||
|
||||
## Send messages using light push
|
||||
|
||||
To send messages in our application, we need to modify the `sendMessage()` function to serialize user input into our Protobuf structure and [push it to the network](/guides/js-waku/light-send-receive#send-messages-using-light-push) using the `useLightPush()` function:
|
||||
|
||||
```js title="src/App.jsx"
|
||||
import { useLightPush } from "@waku/react";
|
||||
|
||||
function App() {
|
||||
// Bind push method to a node and encoder
|
||||
const { push } = useLightPush({ node, encoder });
|
||||
|
||||
// Send the message using Light Push
|
||||
const sendMessage = async () => {
|
||||
if (!push || inputMessage.length === 0) return;
|
||||
|
||||
// Create a new message object
|
||||
const timestamp = Date.now();
|
||||
const protoMessage = DataPacket.create({
|
||||
timestamp: timestamp,
|
||||
message: inputMessage
|
||||
});
|
||||
|
||||
// Serialise the message and push to the network
|
||||
const payload = DataPacket.encode(protoMessage).finish();
|
||||
const { recipients, errors } = await push({ payload, timestamp });
|
||||
|
||||
// Check for errors
|
||||
if (errors.length === 0) {
|
||||
setInputMessage("");
|
||||
console.log("MESSAGE PUSHED");
|
||||
} else {
|
||||
console.log(errors);
|
||||
}
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
## Receive messages using filter
|
||||
|
||||
To display messages in our application, we need to use the `useFilterMessages()` function to create a [Filter subscription](/guides/js-waku/light-send-receive/#receive-messages-using-filter), receive incoming messages, and render them in our interface:
|
||||
|
||||
```js title="src/App.jsx"
|
||||
import { useFilterMessages } from "@waku/react";
|
||||
|
||||
function App() {
|
||||
// Receive messages from Filter subscription
|
||||
const { messages: filterMessages } = useFilterMessages({ node, decoder });
|
||||
|
||||
// Render the list of messages
|
||||
useEffect(() => {
|
||||
setMessages(filterMessages.map((wakuMessage) => {
|
||||
if (!wakuMessage.payload) return;
|
||||
return DataPacket.decode(wakuMessage.payload);
|
||||
}));
|
||||
}, [filterMessages]);
|
||||
}
|
||||
```
|
||||
|
||||
## Retrieve messages using store
|
||||
|
||||
To display messages from the past, we need to retrieve them from the [Store protocol](/guides/js-waku/store-retrieve-messages) using the `useStoreMessages()` function when our application initialises and then render them alongside newly received messages:
|
||||
|
||||
```js title="src/App.jsx"
|
||||
import { useFilterMessages, useStoreMessages } from "@waku/react";
|
||||
|
||||
function App() {
|
||||
// Query Store peers for past messages
|
||||
const { messages: storeMessages } = useStoreMessages({ node, decoder });
|
||||
|
||||
// Receive messages from Filter subscription
|
||||
const { messages: filterMessages } = useFilterMessages({ node, decoder });
|
||||
|
||||
// Render both past and new messages
|
||||
useEffect(() => {
|
||||
const allMessages = storeMessages.concat(filterMessages);
|
||||
setMessages(allMessages.map((wakuMessage) => {
|
||||
if (!wakuMessage.payload) return;
|
||||
return DataPacket.decode(wakuMessage.payload);
|
||||
}));
|
||||
}, [filterMessages, storeMessages]);
|
||||
}
|
||||
```
|
||||
|
||||
You can also configure a specific Store peer when creating the node, which is useful when running your own Store node or using a specific node in the network:
|
||||
|
||||
```js
|
||||
const node = await createLightNode({
|
||||
defaultBootstrap: true,
|
||||
store: {
|
||||
peer: "/ip4/1.2.3.4/tcp/1234/p2p/16Uiu2HAm..." // multiaddr or PeerId of your Store node
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
If the specified Store peer is not available, the node will fall back to using random Store peers in the network.
|
||||
|
||||
:::info
|
||||
To explore the available Store query options, have a look at the [Retrieve Messages Using Store Protocol](/guides/js-waku/store-retrieve-messages#store-query-options) guide.
|
||||
:::
|
||||
|
||||
:::tip
|
||||
You have successfully integrated `@waku/sdk` into a React application using the `@waku/react` package. Have a look at the [web-chat](https://github.com/waku-org/js-waku-examples/tree/master/examples/web-chat) example for a working demo and the [Building a Tic-Tac-Toe Game with Waku](https://blog.waku.org/2024-01-22-tictactoe-tutorial/) tutorial to learn more.
|
||||
:::
|
||||
Reference in New Issue
Block a user