Status Documentation

Embark Into Whisper - Making Gossip Easy

Intro

In this tutorial we’ll learn how to use Ethereum’s Whisper protocol and Embark, creating a chat application even simpler than the one we built in our previous tutorial. We have again setup a Github repository to make it easy for you to follow along.

Embark is a framework for DApp developers that makes building truly decentralized applications easy. It includes support for smart contracts, IPFS and Swarm, Whisper, advanced debugging and testing, front-end code and templates for everything, and an ecosystem of plugins. Please make sure you have the latest version installed. It should be as simple as:

npm install -g embark@latest

Setup

Once you have Embark installed, we are ready to begin coding! Execute these commands in a terminal window to clone the repository and install its dependencies:

git clone https://github.com/status-im/whisper-embark-tutorial.git
cd whisper-embark-tutorial

First run

Now let’s run our website quickly to see all the things Embark will do for us automagically:

embark run

You should see the Embark dashboard in your terminal and be redirected to your browser where Embark will load and then display our skeleton code. In the dashboard we have:

  • Contracts - the top left shows which contracts are deployed and their address.
  • Modules loaded and running - the top right shows the status of the loaded modules running (or not running) in Embark.
  • Log - the middle shows log output.
  • Console - on the bottom row there is a console that will let us interact with web3 and ipfs (try it out by typing help to see available commands).

You’ll notice from the logs and from the modules that Embark has started various processes, and webpacked our site for us. Let’s take a tour of the barebones dApp. It has several features that are not yet hooked up to whisper, but that’s what we’re here to learn how to do.

Coding our dApp

The file ./app/js/index.js is full of TODOs that we need to complete. You’ll notice that using Embark, the amount of boilerplate code you need to write is greatly reduced. The idea is that you should be able to focus the vast majority of your time on your dApp’s business logic.

// TODO: Generate a symmetric key

In our chat application, public messages are sent to a channel represented by a shared symmetric key whose “password” is just the channel we’ll be using and listening to. “Public” messages are messages encrypted using this known symmetric key and topic, since they are not addressed to anyone in particular and are received by anyone that’s listening to a specific channel.

// Generate a symmetric key
const channelSymKey = await web3.shh.generateSymKeyFromPassword(DEFAULT_CHANNEL);

// TODO: Obtain public key

Knowing and displaying to a user their public key (or “contact code”) is useful as this is one means of adding other contacts (ENS being another). Embark already generated a keypair for us, so let’s use web3.shh.getPublicKey to fetch Embark’s keypair:

// Obtain public key
const pubKey = await web3.shh.getPublicKey(EmbarkJS.Messages.currentMessages.sig);

// Send message via whisper

As we already have a keypair available, we’ll only need to implement code for sending and receiving messages. This is done by using EmbarkJS.Messages.sendMessage(options). Unlike using web3.utils.shh directly, Embark will hex-encode the data for us, so you can send plain text as we do here:

// Send message via whisper
EmbarkJS.Messages.sendMessage({
    symKeyID: channelSymKey,
    topic: DEFAULT_CHANNEL, 
    data: message
});

sendMessage accepts an option object that can contain the following attributes:

  1. symKeyID: if specified, it will send the message to this symmetric key id. Otherwise, it will send to a random symmetric key generated by Embark.
  2. pubKey: the public key for message encryption (which is used when sending asymmetric messages). Either symKeyID or pubKey must be present. You cannot have both.
  3. usePrivateKey is a boolean that indicates if you’re going to use an private key for signing the message, or use Embark’s autogenerated random keypair.
  4. privateKeyID: required if you set usePrivateKey to true. Here you send the ID of the signing key.
  5. ttl: the time to live in seconds. The default value is 100.
  6. powTime: the maximal time in seconds to be spent on proof of work. The default value is 3.
  7. powTarget: the minimal PoW target required for this message. The default value is 0.5.
  8. topic: optional when using private keys. It can be an array of strings, or a string that contains the topic for thosee messages. It will be automatically encoded to a 4 bytes hex string(s) by Embark.

// TODO: Subscribe to whisper messages

To receive messages sent via Whisper, you can use EmbarkJS.Messages.listenTo(options); a function Embark provides which helps us obtain messages based on the filters we wish to set. Let’s implement this functionality, calling the function addMessage(data, time) each time a message is received:

// Subscribe to public messages
EmbarkJS.Messages.listenTo({
    topic: [DEFAULT_CHANNEL],
    symKeyID: channelSymKey
  }, (error, message) => {
    if(error){
        alert("Error during subscription");
        return;
    }

    const {data, time} = message;

    addMessage(data, time);
});

Just like with sendMessage, listenTo also accepts an option object, with the following attributes:

  1. symKeyID: if specified, it will listen to messages sent to this symmetric key id. Otherwise, it will use the random symmetric key generated by Embark. Either symKeyID or pubKey must be present. You cannot have both.
  2. usePrivateKey: a boolean that indicates if you wish to listen to messages sent to your keypair.
  3. privateKeyID: required if you set usePrivateKey to true. Here you send the ID of the signing key.
  4. topic: optional when using private keys. Can be an array of strings, or a string that contains the topic for the messages. Will be automatically encoded to a 4 bytes hex string(s).
  5. minPow is the minimal Proof of Work requirement for incoming messages.

After adding this code, open two instances of the chat application and write a message. You’ll see how it gets displayed in both windows.

// TODO: Send private message

We are going to send private messages with a command similar to IRC: /msg 0xcontact_public_key message. In the section were we need to implement this code, we’ve already assigned the contact’s public key to the contactCode variable, and the body of the message in messageContent.

Sending a message to a specific asymmetric public key is similar to sending it to a symmetric key. The difference is that you need to specify the pubKey attribute instead of symKeyId.

// Send private message
EmbarkJS.Messages.sendMessage({
    pubKey: contactCode,
    topic: DEFAULT_CHANNEL,
    data: messageContent
});

// TODO: Subscribe to private messages

Similar to receiving messages from the public channel, we’ll need to create a subscription to receive private messages. We use as a privateKeyID our keyPair in order for the subscription to receive messages that were sent to our public key.

EmbarkJS.Messages.listenTo({
    usePrivateKey: true,
    privateKeyID: EmbarkJS.Messages.currentMessages.sig
}, (error, message) => { 
    if(error){
        alert("Error during subscription");
        return;
    }

    const {data, time} = message; 
    addMessage(data, time);
});

Once you add this code, go ahead and open three instances of our chat application, write a public message in one window, and in the other, copy the public key and send a private message to the account that created the first message. The first and second window will be able to see the message, but the third window will only have received the public message. (Remember the format for sending a private message: /msg 0xcontact_public_key message )

At the moment we aren’t displaying in the chat area where the message originated. We’ll add this functionality in the coming release of Embark, and update this tutorial accordingly.

Final thoughts

Building a dApp that uses Whisper with Embark is amazingly easy!

Something toconsider is that, even through Embark lets you use Whisper in your DApps, it does not mean that anyone will be able to use it when browsing to your dApp. This is because whisper is not enabled by default in most nodes. You’ll need to connect to a node that supports this feature (i.e. geth with the --shh flag).

Things will change in the future once Whisper gains more traction. Let’s make that a reality together by building dApps which communicate between each other via Whisper!

On this page