2021-08-02 05:46:14 +00:00
|
|
|
# Retrieve Messages Using Waku Store
|
|
|
|
|
2021-08-06 04:52:39 +00:00
|
|
|
DApps running on a phone or in a browser are often offline:
|
|
|
|
The browser could be closed or mobile app in the background.
|
2021-08-02 05:46:14 +00:00
|
|
|
|
|
|
|
[Waku Relay](https://rfc.vac.dev/spec/18/) is a gossip protocol.
|
2021-08-05 03:08:01 +00:00
|
|
|
As a user, it means that your peers forward you messages they just received.
|
2021-08-02 05:46:14 +00:00
|
|
|
If you cannot be reached by your peers, then messages are not relayed;
|
2021-08-05 03:08:01 +00:00
|
|
|
relay peers do **not** save messages for later.
|
2021-08-06 04:52:39 +00:00
|
|
|
|
|
|
|
However, [Waku Store](https://rfc.vac.dev/spec/13/) 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:
|
2021-08-09 04:03:07 +00:00
|
|
|
For example, when the dApp starts.
|
2021-08-02 05:46:14 +00:00
|
|
|
|
2021-08-05 03:08:01 +00:00
|
|
|
In this guide, we'll review how you can use Waku Store to retrieve messages.
|
|
|
|
|
2021-08-06 04:52:39 +00:00
|
|
|
|
2021-08-05 03:08:01 +00:00
|
|
|
Before starting, you need to choose a _Content Topic_ for your dApp.
|
|
|
|
Check out the [how to choose a content topic guide](choose-content-topic.md) to learn more about content topics.
|
|
|
|
|
|
|
|
For this guide, we are using a single content topic: `/store-guide/1/news/proto`.
|
|
|
|
|
|
|
|
# Installation
|
|
|
|
|
|
|
|
You can install [js-waku](https://npmjs.com/package/js-waku) using your favorite package manager:
|
|
|
|
|
|
|
|
```shell
|
|
|
|
npm install js-waku
|
|
|
|
```
|
|
|
|
|
|
|
|
# Create Waku Instance
|
|
|
|
|
|
|
|
In order to interact with the Waku network, you first need a Waku instance:
|
|
|
|
|
|
|
|
```js
|
|
|
|
import { Waku } from 'js-waku';
|
|
|
|
|
2021-08-10 01:32:14 +00:00
|
|
|
const wakuNode = await Waku.create({ bootstrap: true });
|
2021-08-05 03:08:01 +00:00
|
|
|
```
|
|
|
|
|
2021-08-13 06:02:54 +00:00
|
|
|
Passing the `bootstrap` option will connect your node to predefined Waku nodes.
|
2021-08-10 01:32:14 +00:00
|
|
|
If you want to bootstrap to your own nodes, you can pass an array of multiaddresses instead:
|
2021-08-05 03:08:01 +00:00
|
|
|
|
|
|
|
```js
|
2021-08-10 01:32:14 +00:00
|
|
|
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'
|
|
|
|
]
|
|
|
|
});
|
2021-08-05 03:08:01 +00:00
|
|
|
```
|
|
|
|
|
2021-09-02 05:01:52 +00:00
|
|
|
# Wait to be connected
|
|
|
|
|
|
|
|
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:
|
|
|
|
|
|
|
|
```js
|
|
|
|
await waku.waitForConnectedPeer();
|
|
|
|
```
|
|
|
|
|
|
|
|
The returned Promise will resolve once you are connected to a Waku Store peer.
|
|
|
|
|
2021-08-05 03:08:01 +00:00
|
|
|
# Use Protobuf
|
|
|
|
|
2021-08-09 04:03:07 +00:00
|
|
|
Waku v2 protocols use [protobuf](https://developers.google.com/protocol-buffers/) [by default](https://rfc.vac.dev/spec/10/).
|
2021-08-05 03:08:01 +00:00
|
|
|
|
2021-08-09 04:03:07 +00:00
|
|
|
Let's review how you can use protobuf to send structured data.
|
|
|
|
|
|
|
|
First, define a data structure.
|
2021-08-05 03:08:01 +00:00
|
|
|
For this guide, we will use a simple news article that contains a date of publication, title and body:
|
|
|
|
|
|
|
|
```js
|
|
|
|
{
|
|
|
|
date: Date;
|
|
|
|
title: string;
|
|
|
|
body: string;
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
To encode and decode protobuf payloads, you can use the [protons](https://www.npmjs.com/package/protons) package.
|
|
|
|
|
|
|
|
## Install Protobuf Library
|
|
|
|
|
|
|
|
First, install protons:
|
|
|
|
|
|
|
|
```shell
|
|
|
|
npm install protons
|
|
|
|
```
|
|
|
|
|
|
|
|
## Protobuf Definition
|
|
|
|
|
|
|
|
Then specify the data structure:
|
|
|
|
|
|
|
|
```js
|
|
|
|
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](https://developers.google.com/protocol-buffers/docs/proto).
|
|
|
|
|
|
|
|
## Decode Messages
|
|
|
|
|
|
|
|
To decode the messages retrieved from a Waku Store node,
|
|
|
|
you need to extract the protobuf payload and decode it using `protons`.
|
|
|
|
|
|
|
|
```js
|
|
|
|
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 };
|
|
|
|
};
|
|
|
|
```
|
|
|
|
|
|
|
|
## Retrieve messages
|
|
|
|
|
|
|
|
You now have all the building blocks to retrieve and decode messages for a store node.
|
|
|
|
|
|
|
|
Retrieve messages from a store node:
|
|
|
|
|
|
|
|
```js
|
|
|
|
const ContentTopic = '/store-guide/1/news/proto';
|
|
|
|
|
|
|
|
waku.store
|
|
|
|
.queryHistory([ContentTopic])
|
|
|
|
.catch((e) => {
|
|
|
|
// Be sure to catch any potential error
|
|
|
|
console.log('Failed to retrieve messages', e);
|
|
|
|
})
|
|
|
|
.then((retrievedMessages) => {
|
|
|
|
const articles = retrievedMessages
|
|
|
|
.map(decodeWakuMessage) // Decode messages
|
|
|
|
.filter(Boolean); // Filter out undefined values
|
|
|
|
|
|
|
|
console.log(`${articles.length} articles have been retrieved`);
|
|
|
|
});
|
|
|
|
```
|
|
|
|
|
|
|
|
Note that `WakuStore.queryHistory` select an available store node for you.
|
2021-08-05 06:34:50 +00:00
|
|
|
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.
|
2021-08-05 03:08:01 +00:00
|
|
|
|