whisper: clean up and integrate topics
This commit is contained in:
parent
7b501906db
commit
9a53390f49
|
@ -19,7 +19,7 @@ import (
|
||||||
type Envelope struct {
|
type Envelope struct {
|
||||||
Expiry uint32 // Whisper protocol specifies int32, really should be int64
|
Expiry uint32 // Whisper protocol specifies int32, really should be int64
|
||||||
TTL uint32 // ^^^^^^
|
TTL uint32 // ^^^^^^
|
||||||
Topics [][]byte
|
Topics []Topic
|
||||||
Data []byte
|
Data []byte
|
||||||
Nonce uint32
|
Nonce uint32
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ type Envelope struct {
|
||||||
|
|
||||||
// NewEnvelope wraps a Whisper message with expiration and destination data
|
// NewEnvelope wraps a Whisper message with expiration and destination data
|
||||||
// included into an envelope for network forwarding.
|
// included into an envelope for network forwarding.
|
||||||
func NewEnvelope(ttl time.Duration, topics [][]byte, msg *Message) *Envelope {
|
func NewEnvelope(ttl time.Duration, topics []Topic, msg *Message) *Envelope {
|
||||||
return &Envelope{
|
return &Envelope{
|
||||||
Expiry: uint32(time.Now().Add(ttl).Unix()),
|
Expiry: uint32(time.Now().Add(ttl).Unix()),
|
||||||
TTL: uint32(ttl.Seconds()),
|
TTL: uint32(ttl.Seconds()),
|
||||||
|
|
|
@ -5,6 +5,6 @@ import "crypto/ecdsa"
|
||||||
type Filter struct {
|
type Filter struct {
|
||||||
To *ecdsa.PublicKey
|
To *ecdsa.PublicKey
|
||||||
From *ecdsa.PublicKey
|
From *ecdsa.PublicKey
|
||||||
Topics [][]byte
|
Topics []Topic
|
||||||
Fn func(*Message)
|
Fn func(*Message)
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,8 +75,13 @@ func (self *Message) Wrap(pow time.Duration, options Options) (*Envelope, error)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Convert the user topic into whisper ones
|
||||||
|
topics := make([]Topic, len(options.Topics))
|
||||||
|
for i, topic := range options.Topics {
|
||||||
|
topics[i] = NewTopic(topic)
|
||||||
|
}
|
||||||
// Wrap the processed message, seal it and return
|
// Wrap the processed message, seal it and return
|
||||||
envelope := NewEnvelope(options.TTL, options.Topics, self)
|
envelope := NewEnvelope(options.TTL, topics, self)
|
||||||
envelope.Seal(pow)
|
envelope.Seal(pow)
|
||||||
|
|
||||||
return envelope, nil
|
return envelope, nil
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
// Contains the Whisper protocol Topic element. For formal details please see
|
||||||
|
// the specs at https://github.com/ethereum/wiki/wiki/Whisper-PoC-1-Protocol-Spec#topics.
|
||||||
|
|
||||||
|
package whisper
|
||||||
|
|
||||||
|
import "github.com/ethereum/go-ethereum/crypto"
|
||||||
|
|
||||||
|
// Topic represents a cryptographically secure, probabilistic partial
|
||||||
|
// classifications of a message, determined as the first (left) 4 bytes of the
|
||||||
|
// SHA3 hash of some arbitrary data given by the original author of the message.
|
||||||
|
type Topic [4]byte
|
||||||
|
|
||||||
|
// NewTopic creates a topic from the 4 byte prefix of the SHA3 hash of the data.
|
||||||
|
func NewTopic(data []byte) Topic {
|
||||||
|
prefix := [4]byte{}
|
||||||
|
copy(prefix[:], crypto.Sha3(data)[:4])
|
||||||
|
return Topic(prefix)
|
||||||
|
}
|
||||||
|
|
||||||
|
// String converts a topic byte array to a string representation.
|
||||||
|
func (self *Topic) String() string {
|
||||||
|
return string(self[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
// TopicSet represents a hash set to check if a topic exists or not.
|
||||||
|
type TopicSet map[string]struct{}
|
||||||
|
|
||||||
|
// NewTopicSet creates a topic hash set from a slice of topics.
|
||||||
|
func NewTopicSet(topics []Topic) TopicSet {
|
||||||
|
set := make(map[string]struct{})
|
||||||
|
for _, topic := range topics {
|
||||||
|
set[topic.String()] = struct{}{}
|
||||||
|
}
|
||||||
|
return TopicSet(set)
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
package whisper
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var topicCreationTests = []struct {
|
||||||
|
data []byte
|
||||||
|
hash [4]byte
|
||||||
|
}{
|
||||||
|
{hash: [4]byte{0xc5, 0xd2, 0x46, 0x01}, data: nil},
|
||||||
|
{hash: [4]byte{0xc5, 0xd2, 0x46, 0x01}, data: []byte{}},
|
||||||
|
{hash: [4]byte{0x8f, 0x9a, 0x2b, 0x7d}, data: []byte("test name")},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTopicCreation(t *testing.T) {
|
||||||
|
for i, tt := range topicCreationTests {
|
||||||
|
topic := NewTopic(tt.data)
|
||||||
|
if bytes.Compare(topic[:], tt.hash[:]) != 0 {
|
||||||
|
t.Errorf("test %d: hash mismatch: have %v, want %v.", i, topic, tt.hash)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTopicSetCreation(t *testing.T) {
|
||||||
|
topics := make([]Topic, len(topicCreationTests))
|
||||||
|
for i, tt := range topicCreationTests {
|
||||||
|
topics[i] = NewTopic(tt.data)
|
||||||
|
}
|
||||||
|
set := NewTopicSet(topics)
|
||||||
|
for i, tt := range topicCreationTests {
|
||||||
|
topic := NewTopic(tt.data)
|
||||||
|
if _, ok := set[topic.String()]; !ok {
|
||||||
|
t.Errorf("topic %d: not found in set", i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,36 +0,0 @@
|
||||||
package whisper
|
|
||||||
|
|
||||||
import "github.com/ethereum/go-ethereum/crypto"
|
|
||||||
|
|
||||||
func hashTopic(topic []byte) []byte {
|
|
||||||
return crypto.Sha3(topic)[:4]
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE this isn't DRY, but I don't want to iterate twice.
|
|
||||||
|
|
||||||
// Returns a formatted topics byte slice.
|
|
||||||
// data: unformatted data (e.g., no hashes needed)
|
|
||||||
func Topics(data [][]byte) [][]byte {
|
|
||||||
d := make([][]byte, len(data))
|
|
||||||
for i, byts := range data {
|
|
||||||
d[i] = hashTopic(byts)
|
|
||||||
}
|
|
||||||
return d
|
|
||||||
}
|
|
||||||
|
|
||||||
func TopicsFromString(data ...string) [][]byte {
|
|
||||||
d := make([][]byte, len(data))
|
|
||||||
for i, str := range data {
|
|
||||||
d[i] = hashTopic([]byte(str))
|
|
||||||
}
|
|
||||||
return d
|
|
||||||
}
|
|
||||||
|
|
||||||
func bytesToMap(s [][]byte) map[string]struct{} {
|
|
||||||
m := make(map[string]struct{})
|
|
||||||
for _, topic := range s {
|
|
||||||
m[string(topic)] = struct{}{}
|
|
||||||
}
|
|
||||||
|
|
||||||
return m
|
|
||||||
}
|
|
|
@ -119,7 +119,7 @@ func (self *Whisper) Watch(opts Filter) int {
|
||||||
return self.filters.Install(filter.Generic{
|
return self.filters.Install(filter.Generic{
|
||||||
Str1: string(crypto.FromECDSAPub(opts.To)),
|
Str1: string(crypto.FromECDSAPub(opts.To)),
|
||||||
Str2: string(crypto.FromECDSAPub(opts.From)),
|
Str2: string(crypto.FromECDSAPub(opts.From)),
|
||||||
Data: bytesToMap(opts.Topics),
|
Data: NewTopicSet(opts.Topics),
|
||||||
Fn: func(data interface{}) {
|
Fn: func(data interface{}) {
|
||||||
opts.Fn(data.(*Message))
|
opts.Fn(data.(*Message))
|
||||||
},
|
},
|
||||||
|
@ -272,9 +272,9 @@ func (self *Whisper) Protocol() p2p.Protocol {
|
||||||
return self.protocol
|
return self.protocol
|
||||||
}
|
}
|
||||||
|
|
||||||
func createFilter(message *Message, topics [][]byte, key *ecdsa.PrivateKey) filter.Filter {
|
func createFilter(message *Message, topics []Topic, key *ecdsa.PrivateKey) filter.Filter {
|
||||||
return filter.Generic{
|
return filter.Generic{
|
||||||
Str1: string(crypto.FromECDSAPub(&key.PublicKey)), Str2: string(crypto.FromECDSAPub(message.Recover())),
|
Str1: string(crypto.FromECDSAPub(&key.PublicKey)), Str2: string(crypto.FromECDSAPub(message.Recover())),
|
||||||
Data: bytesToMap(topics),
|
Data: NewTopicSet(topics),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue