用Go来做以太坊开发⑨Whisper通信协议

Whisper

Whisper是一种简单的基于点对点身份的消息传递系统,旨在成为下一去中心化的应用程序的构建块。 它旨在以相当的代价提供弹性和隐私。 在接下来的部分中,我们将设置一个支持Whisper的以太坊节点,然后我们将学习如何在Whisper协议上发送和接收加密消息。

连接Whisper客户端

要使用连接Whisper客户端,我们必须首先连接到运行whisper的以太坊节点。 不幸的是,诸如infura之类的公共网关不支持whisper,因为没有金钱动力免费处理这些消息。 Infura可能会在不久的将来支持whisper,但现在我们必须运行我们自己的geth节点。一旦你安装 geth, 运行geth的时候加 --shh flag来支持whisper协议, 并且加 --wsflag和 --rpc,来支持websocket来接收实时信息,

geth --rpc --shh --ws

现在在我们的Go应用程序中,我们将导入在whisper/shhclient中找到的go-ethereum whisper客户端软件包并初始化客户端,使用默认的websocket端口“8546”通过websockets连接我们的本地geth节点。

client, err := shhclient.Dial("ws://127.0.0.1:8546")
if err != nil {
    log.Fatal(err)
}

_ = client // we'll be using this in the 下个章节

现在我们已经拨打了,让我们创建一个密钥对来加密消息,然后再发送消息 在下一章节.

完整代码

Commands

geth --rpc --shh --ws

whisper_client.go

package main

import (
    "log"

    "github.com/ethereum/go-ethereum/whisper/shhclient"
)

func main() {
    client, err := shhclient.Dial("ws://127.0.0.1:8546")
    if err != nil {
        log.Fatal(err)
    }

    _ = client // we'll be using this in the 下个章节
    fmt.Println("we have a whisper connection")
}

生成 Whisper 密匙对

在Whisper中,消息必须使用对称或非对称密钥加密,以防止除预期接收者以外的任何人读取消息。

在连接到Whisper客户端后,您需要调用客户端的NewKeyPair方法来生成该节点将管理的新公共和私有对。 此函数的结果将是一个唯一的ID,它引用我们将在接下来的几节中用于加密和解密消息的密钥对。

keyID, err := client.NewKeyPair(context.Background())
if err != nil {
    log.Fatal(err)
}

fmt.Println(keyID) // 0ec5cfe4e215239756054992dbc2e10f011db1cdfc88b9ba6301e2f9ea1b58d2

在下一章节让我们学习如何发送一个加密的消息。

完整代码

Commands

geth --rpc --shh --ws

whisper_keypair.go

package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/whisper/shhclient"
)

func main() {
    client, err := shhclient.Dial("ws://127.0.0.1:8546")
    if err != nil {
        log.Fatal(err)
    }

    keyID, err := client.NewKeyPair(context.Background())
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println(keyID) // 0ec5cfe4e215239756054992dbc2e10f011db1cdfc88b9ba6301e2f9ea1b58d2
}

在Whisper上发送消息

在我们能够创建消息之前,我们必须首先使用公钥来加密消息。在上个章节中,我们学习了如何使用NewKeyPair函数生成公钥和私钥对,该函数返回了引用该密钥对的密钥ID。 我们现在必须调用PublicKey函数以字节格式读取密钥对的公钥,我们将使用它来加密消息。

publicKey, err := client.PublicKey(context.Background(), keyID)
if err != nil {
    log.Print(err)
}

fmt.Println(hexutil.Encode(publicKey)) // 0x04f17356fd52b0d13e5ede84f998d26276f1fc9d08d9e73dcac6ded5f3553405db38c2f257c956f32a0c1fca4c3ff6a38a2c277c1751e59a574aecae26d3bf5d1d

现在我们将通过从go-ethereumwhisper/whisperv6包中初始化NewMessage结构来构造我们的私语消息,这需要以下属性:

  • Payload 字节格式的消息内容
  • PublicKey 加密的公钥
  • TTL 消息的活跃时间
  • PowTime 做工证明的时间上限
  • PowTarget 做工证明的时间下限
message := whisperv6.NewMessage{
  Payload:   []byte("Hello"),
  PublicKey: publicKey,
  TTL:       60,
  PowTime:   2,
  PowTarget: 2.5,
}

我们现在可以通过调用客户端的Post函数向网络广播,给它消息,它是否会返回消息的哈希值。

messageHash, err := client.Post(context.Background(), message)
if err != nil {
    log.Fatal(err)
}

fmt.Println(messageHash) // 0xdbfc815d3d122a90d7fb44d1fc6a46f3d76ec752f3f3d04230fe5f1b97d2209a

在下个章节中我们将看到如何创建消息订阅以便能够实时接收消息。

完整代码

Commands

geth --shh --rpc --ws

whisper_send.go

package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/common/hexutil"
    "github.com/ethereum/go-ethereum/whisper/shhclient"
    "github.com/ethereum/go-ethereum/whisper/whisperv6"
)

func main() {
    client, err := shhclient.Dial("ws://127.0.0.1:8546")
    if err != nil {
        log.Fatal(err)
    }

    keyID, err := client.NewKeyPair(context.Background())
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(keyID) // 0ec5cfe4e215239756054992dbc2e10f011db1cdfc88b9ba6301e2f9ea1b58d2

    publicKey, err := client.PublicKey(context.Background(), keyID)
    if err != nil {
        log.Print(err)
    }
    fmt.Println(hexutil.Encode(publicKey)) // 0x04f17356fd52b0d13e5ede84f998d26276f1fc9d08d9e73dcac6ded5f3553405db38c2f257c956f32a0c1fca4c3ff6a38a2c277c1751e59a574aecae26d3bf5d1d

    message := whisperv6.NewMessage{
        Payload:   []byte("Hello"),
        PublicKey: publicKey,
        TTL:       60,
        PowTime:   2,
        PowTarget: 2.5,
    }
    messageHash, err := client.Post(context.Background(), message)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(messageHash) // 0xdbfc815d3d122a90d7fb44d1fc6a46f3d76ec752f3f3d04230fe5f1b97d2209a
}

监听/订阅Whisper消息

在本节中,我们将订阅websockets上的Whisper消息。 我们首先需要的是一个通道,它将从whisper/whisperv6包中的Message类型接收Whispe消息。

messages := make(chan *whisperv6.Message)

在我们调用订阅之前,我们首先需要确定消息的过滤标准。 从whisperv6包中初始化一个新的Criteria对象。 由于我们只对定位到我们的消息感兴趣,因此我们将条件对象上的PrivateKeyID属性设置为我们用于加密消息的相同密钥ID。

criteria := whisperv6.Criteria{
    PrivateKeyID: keyID,
}

接下来,我们调用客户端的SubscribeMessages方法,该方法订阅符合给定条件的消息。 HTTP不支持此方法; 仅支持双向连接,例如websockets和IPC。 最后一个参数是我们之前创建的消息通道。

sub, err := client.SubscribeMessages(context.Background(), criteria, messages)
if err != nil {
  log.Fatal(err)
}

现在我们已经订阅了,我们可以使用select语句来读取消息,并处理订阅中的错误。 如果您从上一节回忆起来,消息内容在Payload属性中作为字节切片,我们可以将其转换回人类可读的字符串。

for {
    select {
    case err := <-sub.Err():
        log.Fatal(err)
    case message := <-messages:
        fmt.Printf(string(message.Payload)) // "Hello"
    }
}

查看下面的完整代码,获取完整的栗子。 这就是消息订阅的所有内容。

完整代码

Commands

geth --shh --rpc --ws

whisper_subscribe.go

package main

import (
    "context"
    "fmt"
    "log"
    "os"
    "runtime"

    "github.com/ethereum/go-ethereum/common/hexutil"
    "github.com/ethereum/go-ethereum/whisper/shhclient"
    "github.com/ethereum/go-ethereum/whisper/whisperv6"
)

func main() {
    client, err := shhclient.Dial("ws://127.0.0.1:8546")
    if err != nil {
        log.Fatal(err)
    }

    keyID, err := client.NewKeyPair(context.Background())
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(keyID) // 0ec5cfe4e215239756054992dbc2e10f011db1cdfc88b9ba6301e2f9ea1b58d2

    messages := make(chan *whisperv6.Message)
    criteria := whisperv6.Criteria{
        PrivateKeyID: keyID,
    }
    sub, err := client.SubscribeMessages(context.Background(), criteria, messages)
    if err != nil {
        log.Fatal(err)
    }

    go func() {
        for {
            select {
            case err := <-sub.Err():
                log.Fatal(err)
            case message := <-messages:
                fmt.Printf(string(message.Payload)) // "Hello"
                os.Exit(0)
            }
        }
    }()

    publicKey, err := client.PublicKey(context.Background(), keyID)
    if err != nil {
        log.Print(err)
    }
    fmt.Println(hexutil.Encode(publicKey)) // 0x04f17356fd52b0d13e5ede84f998d26276f1fc9d08d9e73dcac6ded5f3553405db38c2f257c956f32a0c1fca4c3ff6a38a2c277c1751e59a574aecae26d3bf5d1d

    message := whisperv6.NewMessage{
        Payload:   []byte("Hello"),
        PublicKey: publicKey,
        TTL:       60,
        PowTime:   2,
        PowTarget: 2.5,
    }

    messageHash, err := client.Post(context.Background(), message)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(messageHash) // 0xdbfc815d3d122a90d7fb44d1fc6a46f3d76ec752f3f3d04230fe5f1b97d2209a

    runtime.Goexit() // wait for goroutines to finish
}

添加小编微信:grey0805,加入知识学习小分队~!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,743评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,296评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,285评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,485评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,581评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,821评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,960评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,719评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,186评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,516评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,650评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,329评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,936评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,757评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,991评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,370评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,527评论 2 349

推荐阅读更多精彩内容