aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--whisper/envelope.go4
-rw-r--r--whisper/filter.go2
-rw-r--r--whisper/message.go7
-rw-r--r--whisper/topic.go35
-rw-r--r--whisper/topic_test.go38
-rw-r--r--whisper/util.go36
-rw-r--r--whisper/whisper.go6
7 files changed, 85 insertions, 43 deletions
diff --git a/whisper/envelope.go b/whisper/envelope.go
index c51c6e600..93e3ea1a3 100644
--- a/whisper/envelope.go
+++ b/whisper/envelope.go
@@ -19,7 +19,7 @@ import (
type Envelope struct {
Expiry uint32 // Whisper protocol specifies int32, really should be int64
TTL uint32 // ^^^^^^
- Topics [][]byte
+ Topics []Topic
Data []byte
Nonce uint32
@@ -28,7 +28,7 @@ type Envelope struct {
// NewEnvelope wraps a Whisper message with expiration and destination data
// 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{
Expiry: uint32(time.Now().Add(ttl).Unix()),
TTL: uint32(ttl.Seconds()),
diff --git a/whisper/filter.go b/whisper/filter.go
index b33f2c1a2..7258de3e7 100644
--- a/whisper/filter.go
+++ b/whisper/filter.go
@@ -5,6 +5,6 @@ import "crypto/ecdsa"
type Filter struct {
To *ecdsa.PublicKey
From *ecdsa.PublicKey
- Topics [][]byte
+ Topics []Topic
Fn func(*Message)
}
diff --git a/whisper/message.go b/whisper/message.go
index 457cf6def..ad31aa592 100644
--- a/whisper/message.go
+++ b/whisper/message.go
@@ -75,8 +75,13 @@ func (self *Message) Wrap(pow time.Duration, options Options) (*Envelope, error)
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
- envelope := NewEnvelope(options.TTL, options.Topics, self)
+ envelope := NewEnvelope(options.TTL, topics, self)
envelope.Seal(pow)
return envelope, nil
diff --git a/whisper/topic.go b/whisper/topic.go
new file mode 100644
index 000000000..10069c902
--- /dev/null
+++ b/whisper/topic.go
@@ -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)
+}
diff --git a/whisper/topic_test.go b/whisper/topic_test.go
new file mode 100644
index 000000000..4626e2ae5
--- /dev/null
+++ b/whisper/topic_test.go
@@ -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)
+ }
+ }
+}
diff --git a/whisper/util.go b/whisper/util.go
deleted file mode 100644
index 7a222395f..000000000
--- a/whisper/util.go
+++ /dev/null
@@ -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
-}
diff --git a/whisper/whisper.go b/whisper/whisper.go
index ad29fe16a..d9affe09b 100644
--- a/whisper/whisper.go
+++ b/whisper/whisper.go
@@ -119,7 +119,7 @@ func (self *Whisper) Watch(opts Filter) int {
return self.filters.Install(filter.Generic{
Str1: string(crypto.FromECDSAPub(opts.To)),
Str2: string(crypto.FromECDSAPub(opts.From)),
- Data: bytesToMap(opts.Topics),
+ Data: NewTopicSet(opts.Topics),
Fn: func(data interface{}) {
opts.Fn(data.(*Message))
},
@@ -272,9 +272,9 @@ func (self *Whisper) Protocol() p2p.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{
Str1: string(crypto.FromECDSAPub(&key.PublicKey)), Str2: string(crypto.FromECDSAPub(message.Recover())),
- Data: bytesToMap(topics),
+ Data: NewTopicSet(topics),
}
}