How to Whisper

Whisper is a pure identity-based messaging system providing a low-level (non-application-specific) but easily-accessible API without being based upon or prejudiced by the low-level hardware attributes and characteristics, particularly the notion of singular endpoints.

This tutorial assumes you've read [p2p 101] (if you haven't yet read it, we recommend that you do so to familiarize yourself before continuing). This tutorial will help guide you with setting up a full p2p server with whisper capabilities.

Let's quickly cover some of Whisper's basic functionalities and discuss in greater detail.

whisper.Send(myEnvelope)

The notion of envelopes and messages in Whisper is somewhat blurred. An application shouldn't ever need to know the difference between the two - it should only care about the information it needs. Therefore, Whisper comes packed with a subscribing mechanism that allows you to watch and listen for specific Whisper messages (i.e. to you or with a specific topic).

whisper.Watch(Filter{
        From: myFriendsPubKey,
        Fn: func(msg *whisper.Message) { /* ... */ },
})

Envelopes & Messages

Whenever you want to send messages over the Whisper network, you need to prove that you've done some significant work for sealing the message (such as the cost for sending messages) - the more work you put into sealing it, the higher the priority the message will have when propagating it over the network.

Whisper's Proof of Work mechanism consists of a simple SHA3 algorithm in which we try to find the smallest number within a given time frame. Giving the algorithm, more time will result in a smaller number, which means the message has a higher priority in the network.

Messages are also sealed with a Time To Live. Whisper peers will automatically flush out messages which have exceeded their time to live (with a maximum timeframe of up to 2 days).

Additionally, messages may also contain a recipient (public key) and a set of topics. Topics will allow us to specify messages with their subject (e.g., "shoes", "laptop", "marketplace", "chat"). Topics are automatically hashed, and only the first 4 bytes are used during transmission - as such, topics are not 100% reliable, so they should be treated as a probabilistic message filter.

Sending a Whisper message requires you to:

  1. Create a new whisper.Message

  2. Seal it (optionally encrypt, sign and supply with topics)

  3. Send it to your peers

topics := TopicsFromString("my", "message")
msg := whisper.NewMessage([]byte("hello world"))  // 1
envelope := msg.Seal(whisper.Opts{                // 2
        From:   myPrivateKey, // Sign it
        Topics: topics,
})
whisper.Send(envelope)                            // 3

Whenever a message needs to be encrypted for a specific recipient supply, the Opts is struct with an additional To parameter, which accepts the recipient's public key (ecdsa.PublicKey).

Watching & Listening

Watching for specific messages on the whisper network can be done using the Watch method. You have the option to watch for messages from a specific recipient, with specific topics or messages directly sent to you.

topics := TopicsFromString("my", "message")
whisper.Watch(Filter{
        Topics: topics
        Fn:     func(msg *Message) {
                fmt.Println(msg)
        },
})

Connecting it all together

Tying everything together and supplying Whisper as a sub-protocol to the DEV's P2P service, we have Whisper including peer handling and message propagation.

package main

import (
	"fmt"
	"log"
	"os"

	"github.com/elastos/Elastos.ELA.SideChain.ESC/p2p"
	"github.com/elastos/Elastos.ELA.SideChain.ESC/whisper"
	"github.com/obscuren/secp256k1-go"
)

func main() {
	pub, _ := secp256k1.GenerateKeyPair()

	whisper := whisper.New()

	srv := p2p.Server{
		MaxPeers:   10,
		Identity:   p2p.NewSimpleClientIdentity("my-whisper-app", "1.0", "", string(pub)),
		ListenAddr: ":8000",
		Protocols: []p2p.Protocol{whisper.Protocol()},
	}
	if err := srv.Start(); err != nil {
		fmt.Println("could not start server:", err)
		os.Exit(1)
	}

	select {}
}

Last updated