whisper是一个比较独立的模块,它的运行完全独立于geth也就是以太坊区块链.如果要感受whisper可以通过启动wnode来感受.wnode有丰富的参数,能够让大家加入whisper网络,来感受这个去中心化的聊天系统. 我前面写的[以太坊系列之十八: 百行go代码构建p2p聊天室](http://www.cnblogs.com/baizx/p/7505096.html)就是一个简版的wnode,只取了其中一小部分.
whisper实际上就是以太坊p2p网络上的一个应用, 注意我强调以太坊网络是因为它的p2p协议直接取自前面介绍的[以太坊系列之六: p2p模块--以太坊源码学习](http://www.cnblogs.com/baizx/p/6963636.html),那里提到P2p模块是支持自定义Protocol的,实际上以太坊区块的同步,tx的发布都是其中的一个协议而已.同样whisper也是其中的一个协议.
我们来看看这个协议的定义.
```go
// p2p whisper sub protocol handler
whisper.protocol = p2p.Protocol{
Name: ProtocolName, //shh
Version: uint(ProtocolVersion), //5
Length: NumberOfMessageCodes, //64
Run: whisper.HandlePeer, //数据处理
}
```
在web3 api中暴露的shh模块主体就是在这里
以太坊系列对外提供的api接口都是通过rpc调用来实现的. whisper同样也是,whisper对外提供的api主要是建立账户,发送消息,过滤消息等. 我们一般意义上的接收消息就是过滤消息,因为p2p网络中会收到大量各种消息,你只有定义了自己感兴趣的消息,也就是过滤消息才能得到想要的具体消息内容.
对外提供rpc api的接口一般都是如下形式:
```go
[]rpc.API{
{
Namespace: ProtocolName, //shh
Version: ProtocolVersionStr, //5.0
Service: NewPublicWhisperAPI(w),
Public: true,
},
}
// NewPublicWhisperAPI create a new RPC whisper service.
func NewPublicWhisperAPI(w *Whisper) *PublicWhisperAPI {
return &PublicWhisperAPI{whisper: w}
}
```
PublicWhisperAPI包含了所有可能被访问的api.
主要有三种消息,分别是messagesCode ,p2pCode,p2pRequestCode
我们一般用的主要就是messagesCode类型,p2pCode用于点对点直接通信,p2pRequestCode为智能合约使用.
主要说一下messageCode类型如何处理.
对于收到的消息,首先检验发送时间是否正确(防止错误垃圾消息),然后计算工作量是否到位(这个就是bitmessage的核心思想),
第三步检验该消息是否已经收到过(hash值存在),如果存在就忽略,否则进入第四步.
第四步将消息放入本地消息队列 节点会定期对消息队列中的消息进行处理.
第五步 检测是否满足某个过滤条件,如果是,就将消息放入过滤队列(channel)中.
另外,节点还会启动一个携程定期把本地收到的合法消息广播出去.
以上基本就是一个p2p聊天系统的核心了.
消息有两种加密形式,一种是对称的aes加密,另一种是非对称的ecdsaj加密.上面对于消息的处理流程中除了第五步检测是否满足设定的过滤条件,其他步骤都不涉及对消息内容的处理,也就无需解密.
也就是说真正对消息内容的处理,是在过滤器中进行的.先看看消息的格式,里面包含什么内容. 分成两个部分,一个是信封,一个是信体,信封没有加密,信体加密.
```go
// Envelope represents a clear-text data packet to transmit through the Whisper
// network. Its contents may or may not be encrypted and signed.
type Envelope struct {
Version []byte
Expiry uint32
TTL uint32
Topic TopicType
AESNonce []byte
Data []byte
EnvNonce uint64
pow float64 // Message-specific PoW as described in the Whisper specification.
}
```
信封里主要是时间相关信息以及验证工作量所需信息. 需要特别指出的是,whisper可以按照特定的topic过滤消息,topic是明文且没有加密的四字节数据.
信体格式比较复杂,关键是里面有需要解密消息的对称密钥或者公钥.
```go
// ReceivedMessage represents a data packet to be received through the
// Whisper protocol.
type ReceivedMessage struct {
Raw []byte
Payload []byte
Padding []byte
Signature []byte
PoW float64 // Proof of work as described in the Whisper spec
Sent uint32 // Time when the message was posted into the network
TTL uint32 // Maximum time to live allowed for the message
Src *ecdsa.PublicKey // Message recipient (identity used to decode the message)
Dst *ecdsa.PublicKey // Message recipient (identity used to decode the message)
Topic TopicType
SymKeyHash common.Hash // The Keccak256Hash of the key, associated with the Topic
EnvelopeHash common.Hash // Message envelope hash to act as a unique id
EnvelopeVersion uint64
}
```
可以看到信体里也把信封的大部分内容都包含进去了.
过滤器必须给出一个公钥或者aes密钥来解密消息, 也可以给出一个或多个可选的topic来进行过滤.
过滤器实际上是用户接入whisper消息系统的入口,也只有这种方式来接收消息.
另外过滤器还可以指定一个更高难度(相比默认难度)的工作量来过滤消息.
过滤器结构如下:
type Filter struct {
Src *ecdsa.PublicKey // Sender of the message
KeyAsym *ecdsa.PrivateKey // Private Key of recipient
KeySym []byte // Key associated with the Topic
Topics [][]byte // Topics to filter messages with
PoW float64 // Proof of work as described in the Whisper spec
AllowP2P bool // Indicates whether this filter is interested in direct peer-to-peer messages
SymKeyHash common.Hash // The Keccak256Hash of the symmetric key, needed for optimization
msgChan chan *ReceivedMessage //matched message channel
mutex sync.RWMutex
}
主要包含指定的公钥,指定的对称密钥,topics,pow.