forked from mirror/go-ethereum
Conflicts: rpc/api.gorelease/1.0.1
commit
99027c79fe
@ -0,0 +1,142 @@ |
||||
package whisper |
||||
|
||||
import ( |
||||
"bytes" |
||||
"testing" |
||||
"time" |
||||
|
||||
"github.com/ethereum/go-ethereum/crypto" |
||||
"github.com/ethereum/go-ethereum/crypto/ecies" |
||||
) |
||||
|
||||
func TestEnvelopeOpen(t *testing.T) { |
||||
payload := []byte("hello world") |
||||
message := NewMessage(payload) |
||||
|
||||
envelope, err := message.Wrap(DefaultPoW, Options{}) |
||||
if err != nil { |
||||
t.Fatalf("failed to wrap message: %v", err) |
||||
} |
||||
opened, err := envelope.Open(nil) |
||||
if err != nil { |
||||
t.Fatalf("failed to open envelope: %v", err) |
||||
} |
||||
if opened.Flags != message.Flags { |
||||
t.Fatalf("flags mismatch: have %d, want %d", opened.Flags, message.Flags) |
||||
} |
||||
if bytes.Compare(opened.Signature, message.Signature) != 0 { |
||||
t.Fatalf("signature mismatch: have 0x%x, want 0x%x", opened.Signature, message.Signature) |
||||
} |
||||
if bytes.Compare(opened.Payload, message.Payload) != 0 { |
||||
t.Fatalf("payload mismatch: have 0x%x, want 0x%x", opened.Payload, message.Payload) |
||||
} |
||||
if opened.Sent.Unix() != message.Sent.Unix() { |
||||
t.Fatalf("send time mismatch: have %d, want %d", opened.Sent, message.Sent) |
||||
} |
||||
if opened.TTL/time.Second != DefaultTTL/time.Second { |
||||
t.Fatalf("message TTL mismatch: have %v, want %v", opened.TTL, DefaultTTL) |
||||
} |
||||
|
||||
if opened.Hash != envelope.Hash() { |
||||
t.Fatalf("message hash mismatch: have 0x%x, want 0x%x", opened.Hash, envelope.Hash()) |
||||
} |
||||
} |
||||
|
||||
func TestEnvelopeAnonymousOpenUntargeted(t *testing.T) { |
||||
payload := []byte("hello envelope") |
||||
envelope, err := NewMessage(payload).Wrap(DefaultPoW, Options{}) |
||||
if err != nil { |
||||
t.Fatalf("failed to wrap message: %v", err) |
||||
} |
||||
opened, err := envelope.Open(nil) |
||||
if err != nil { |
||||
t.Fatalf("failed to open envelope: %v", err) |
||||
} |
||||
if opened.To != nil { |
||||
t.Fatalf("recipient mismatch: have 0x%x, want nil", opened.To) |
||||
} |
||||
if bytes.Compare(opened.Payload, payload) != 0 { |
||||
t.Fatalf("payload mismatch: have 0x%x, want 0x%x", opened.Payload, payload) |
||||
} |
||||
} |
||||
|
||||
func TestEnvelopeAnonymousOpenTargeted(t *testing.T) { |
||||
key, err := crypto.GenerateKey() |
||||
if err != nil { |
||||
t.Fatalf("failed to generate test identity: %v", err) |
||||
} |
||||
|
||||
payload := []byte("hello envelope") |
||||
envelope, err := NewMessage(payload).Wrap(DefaultPoW, Options{ |
||||
To: &key.PublicKey, |
||||
}) |
||||
if err != nil { |
||||
t.Fatalf("failed to wrap message: %v", err) |
||||
} |
||||
opened, err := envelope.Open(nil) |
||||
if err != nil { |
||||
t.Fatalf("failed to open envelope: %v", err) |
||||
} |
||||
if opened.To != nil { |
||||
t.Fatalf("recipient mismatch: have 0x%x, want nil", opened.To) |
||||
} |
||||
if bytes.Compare(opened.Payload, payload) == 0 { |
||||
t.Fatalf("payload match, should have been encrypted: 0x%x", opened.Payload) |
||||
} |
||||
} |
||||
|
||||
func TestEnvelopeIdentifiedOpenUntargeted(t *testing.T) { |
||||
key, err := crypto.GenerateKey() |
||||
if err != nil { |
||||
t.Fatalf("failed to generate test identity: %v", err) |
||||
} |
||||
|
||||
payload := []byte("hello envelope") |
||||
envelope, err := NewMessage(payload).Wrap(DefaultPoW, Options{}) |
||||
if err != nil { |
||||
t.Fatalf("failed to wrap message: %v", err) |
||||
} |
||||
opened, err := envelope.Open(key) |
||||
switch err { |
||||
case nil: |
||||
t.Fatalf("envelope opened with bad key: %v", opened) |
||||
|
||||
case ecies.ErrInvalidPublicKey: |
||||
// Ok, key mismatch but opened
|
||||
|
||||
default: |
||||
t.Fatalf("failed to open envelope: %v", err) |
||||
} |
||||
|
||||
if opened.To != nil { |
||||
t.Fatalf("recipient mismatch: have 0x%x, want nil", opened.To) |
||||
} |
||||
if bytes.Compare(opened.Payload, payload) != 0 { |
||||
t.Fatalf("payload mismatch: have 0x%x, want 0x%x", opened.Payload, payload) |
||||
} |
||||
} |
||||
|
||||
func TestEnvelopeIdentifiedOpenTargeted(t *testing.T) { |
||||
key, err := crypto.GenerateKey() |
||||
if err != nil { |
||||
t.Fatalf("failed to generate test identity: %v", err) |
||||
} |
||||
|
||||
payload := []byte("hello envelope") |
||||
envelope, err := NewMessage(payload).Wrap(DefaultPoW, Options{ |
||||
To: &key.PublicKey, |
||||
}) |
||||
if err != nil { |
||||
t.Fatalf("failed to wrap message: %v", err) |
||||
} |
||||
opened, err := envelope.Open(key) |
||||
if err != nil { |
||||
t.Fatalf("failed to open envelope: %v", err) |
||||
} |
||||
if opened.To != nil { |
||||
t.Fatalf("recipient mismatch: have 0x%x, want nil", opened.To) |
||||
} |
||||
if bytes.Compare(opened.Payload, payload) != 0 { |
||||
t.Fatalf("payload mismatch: have 0x%x, want 0x%x", opened.Payload, payload) |
||||
} |
||||
} |
@ -0,0 +1,199 @@ |
||||
package whisper |
||||
|
||||
import ( |
||||
"bytes" |
||||
|
||||
"testing" |
||||
) |
||||
|
||||
var filterTopicsCreationTests = []struct { |
||||
topics [][]string |
||||
filter [][][4]byte |
||||
}{ |
||||
{ // Simple topic filter
|
||||
topics: [][]string{ |
||||
{"abc", "def", "ghi"}, |
||||
{"def"}, |
||||
{"ghi", "abc"}, |
||||
}, |
||||
filter: [][][4]byte{ |
||||
{{0x4e, 0x03, 0x65, 0x7a}, {0x34, 0x60, 0x7c, 0x9b}, {0x21, 0x41, 0x7d, 0xf9}}, |
||||
{{0x34, 0x60, 0x7c, 0x9b}}, |
||||
{{0x21, 0x41, 0x7d, 0xf9}, {0x4e, 0x03, 0x65, 0x7a}}, |
||||
}, |
||||
}, |
||||
{ // Wild-carded topic filter
|
||||
topics: [][]string{ |
||||
{"abc", "def", "ghi"}, |
||||
{}, |
||||
{""}, |
||||
{"def"}, |
||||
}, |
||||
filter: [][][4]byte{ |
||||
{{0x4e, 0x03, 0x65, 0x7a}, {0x34, 0x60, 0x7c, 0x9b}, {0x21, 0x41, 0x7d, 0xf9}}, |
||||
{}, |
||||
{}, |
||||
{{0x34, 0x60, 0x7c, 0x9b}}, |
||||
}, |
||||
}, |
||||
} |
||||
|
||||
var filterTopicsCreationFlatTests = []struct { |
||||
topics []string |
||||
filter [][][4]byte |
||||
}{ |
||||
{ // Simple topic list
|
||||
topics: []string{"abc", "def", "ghi"}, |
||||
filter: [][][4]byte{ |
||||
{{0x4e, 0x03, 0x65, 0x7a}}, |
||||
{{0x34, 0x60, 0x7c, 0x9b}}, |
||||
{{0x21, 0x41, 0x7d, 0xf9}}, |
||||
}, |
||||
}, |
||||
{ // Wild-carded topic list
|
||||
topics: []string{"abc", "", "ghi"}, |
||||
filter: [][][4]byte{ |
||||
{{0x4e, 0x03, 0x65, 0x7a}}, |
||||
{}, |
||||
{{0x21, 0x41, 0x7d, 0xf9}}, |
||||
}, |
||||
}, |
||||
} |
||||
|
||||
func TestFilterTopicsCreation(t *testing.T) { |
||||
// Check full filter creation
|
||||
for i, tt := range filterTopicsCreationTests { |
||||
// Check the textual creation
|
||||
filter := NewFilterTopicsFromStrings(tt.topics...) |
||||
if len(filter) != len(tt.topics) { |
||||
t.Errorf("test %d: condition count mismatch: have %v, want %v", i, len(filter), len(tt.topics)) |
||||
continue |
||||
} |
||||
for j, condition := range filter { |
||||
if len(condition) != len(tt.filter[j]) { |
||||
t.Errorf("test %d, condition %d: size mismatch: have %v, want %v", i, j, len(condition), len(tt.filter[j])) |
||||
continue |
||||
} |
||||
for k := 0; k < len(condition); k++ { |
||||
if bytes.Compare(condition[k][:], tt.filter[j][k][:]) != 0 { |
||||
t.Errorf("test %d, condition %d, segment %d: filter mismatch: have 0x%x, want 0x%x", i, j, k, condition[k], tt.filter[j][k]) |
||||
} |
||||
} |
||||
} |
||||
// Check the binary creation
|
||||
binary := make([][][]byte, len(tt.topics)) |
||||
for j, condition := range tt.topics { |
||||
binary[j] = make([][]byte, len(condition)) |
||||
for k, segment := range condition { |
||||
binary[j][k] = []byte(segment) |
||||
} |
||||
} |
||||
filter = NewFilterTopics(binary...) |
||||
if len(filter) != len(tt.topics) { |
||||
t.Errorf("test %d: condition count mismatch: have %v, want %v", i, len(filter), len(tt.topics)) |
||||
continue |
||||
} |
||||
for j, condition := range filter { |
||||
if len(condition) != len(tt.filter[j]) { |
||||
t.Errorf("test %d, condition %d: size mismatch: have %v, want %v", i, j, len(condition), len(tt.filter[j])) |
||||
continue |
||||
} |
||||
for k := 0; k < len(condition); k++ { |
||||
if bytes.Compare(condition[k][:], tt.filter[j][k][:]) != 0 { |
||||
t.Errorf("test %d, condition %d, segment %d: filter mismatch: have 0x%x, want 0x%x", i, j, k, condition[k], tt.filter[j][k]) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
// Check flat filter creation
|
||||
for i, tt := range filterTopicsCreationFlatTests { |
||||
// Check the textual creation
|
||||
filter := NewFilterTopicsFromStringsFlat(tt.topics...) |
||||
if len(filter) != len(tt.topics) { |
||||
t.Errorf("test %d: condition count mismatch: have %v, want %v", i, len(filter), len(tt.topics)) |
||||
continue |
||||
} |
||||
for j, condition := range filter { |
||||
if len(condition) != len(tt.filter[j]) { |
||||
t.Errorf("test %d, condition %d: size mismatch: have %v, want %v", i, j, len(condition), len(tt.filter[j])) |
||||
continue |
||||
} |
||||
for k := 0; k < len(condition); k++ { |
||||
if bytes.Compare(condition[k][:], tt.filter[j][k][:]) != 0 { |
||||
t.Errorf("test %d, condition %d, segment %d: filter mismatch: have 0x%x, want 0x%x", i, j, k, condition[k], tt.filter[j][k]) |
||||
} |
||||
} |
||||
} |
||||
// Check the binary creation
|
||||
binary := make([][]byte, len(tt.topics)) |
||||
for j, topic := range tt.topics { |
||||
binary[j] = []byte(topic) |
||||
} |
||||
filter = NewFilterTopicsFlat(binary...) |
||||
if len(filter) != len(tt.topics) { |
||||
t.Errorf("test %d: condition count mismatch: have %v, want %v", i, len(filter), len(tt.topics)) |
||||
continue |
||||
} |
||||
for j, condition := range filter { |
||||
if len(condition) != len(tt.filter[j]) { |
||||
t.Errorf("test %d, condition %d: size mismatch: have %v, want %v", i, j, len(condition), len(tt.filter[j])) |
||||
continue |
||||
} |
||||
for k := 0; k < len(condition); k++ { |
||||
if bytes.Compare(condition[k][:], tt.filter[j][k][:]) != 0 { |
||||
t.Errorf("test %d, condition %d, segment %d: filter mismatch: have 0x%x, want 0x%x", i, j, k, condition[k], tt.filter[j][k]) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
var filterCompareTests = []struct { |
||||
matcher filterer |
||||
message filterer |
||||
match bool |
||||
}{ |
||||
{ // Wild-card filter matching anything
|
||||
matcher: filterer{to: "", from: "", matcher: newTopicMatcher()}, |
||||
message: filterer{to: "to", from: "from", matcher: newTopicMatcher(NewFilterTopicsFromStringsFlat("topic")...)}, |
||||
match: true, |
||||
}, |
||||
{ // Filter matching the to field
|
||||
matcher: filterer{to: "to", from: "", matcher: newTopicMatcher()}, |
||||
message: filterer{to: "to", from: "from", matcher: newTopicMatcher(NewFilterTopicsFromStringsFlat("topic")...)}, |
||||
match: true, |
||||
}, |
||||
{ // Filter rejecting the to field
|
||||
matcher: filterer{to: "to", from: "", matcher: newTopicMatcher()}, |
||||
message: filterer{to: "", from: "from", matcher: newTopicMatcher(NewFilterTopicsFromStringsFlat("topic")...)}, |
||||
match: false, |
||||
}, |
||||
{ // Filter matching the from field
|
||||
matcher: filterer{to: "", from: "from", matcher: newTopicMatcher()}, |
||||
message: filterer{to: "to", from: "from", matcher: newTopicMatcher(NewFilterTopicsFromStringsFlat("topic")...)}, |
||||
match: true, |
||||
}, |
||||
{ // Filter rejecting the from field
|
||||
matcher: filterer{to: "", from: "from", matcher: newTopicMatcher()}, |
||||
message: filterer{to: "to", from: "", matcher: newTopicMatcher(NewFilterTopicsFromStringsFlat("topic")...)}, |
||||
match: false, |
||||
}, |
||||
{ // Filter matching the topic field
|
||||
matcher: filterer{to: "", from: "from", matcher: newTopicMatcher(NewFilterTopicsFromStringsFlat("topic")...)}, |
||||
message: filterer{to: "to", from: "from", matcher: newTopicMatcher(NewFilterTopicsFromStringsFlat("topic")...)}, |
||||
match: true, |
||||
}, |
||||
{ // Filter rejecting the topic field
|
||||
matcher: filterer{to: "", from: "", matcher: newTopicMatcher(NewFilterTopicsFromStringsFlat("topic")...)}, |
||||
message: filterer{to: "to", from: "from", matcher: newTopicMatcher()}, |
||||
match: false, |
||||
}, |
||||
} |
||||
|
||||
func TestFilterCompare(t *testing.T) { |
||||
for i, tt := range filterCompareTests { |
||||
if match := tt.matcher.Compare(tt.message); match != tt.match { |
||||
t.Errorf("test %d: match mismatch: have %v, want %v", i, match, tt.match) |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,84 @@ |
||||
// Contains the external API side message filter for watching, pooling and polling
|
||||
// matched whisper messages, also serializing data access to avoid duplications.
|
||||
|
||||
package xeth |
||||
|
||||
import ( |
||||
"sync" |
||||
"time" |
||||
|
||||
"github.com/ethereum/go-ethereum/common" |
||||
) |
||||
|
||||
// whisperFilter is the message cache matching a specific filter, accumulating
|
||||
// inbound messages until the are requested by the client.
|
||||
type whisperFilter struct { |
||||
id int // Filter identifier for old message retrieval
|
||||
ref *Whisper // Whisper reference for old message retrieval
|
||||
|
||||
cache []WhisperMessage // Cache of messages not yet polled
|
||||
skip map[common.Hash]struct{} // List of retrieved messages to avoid duplication
|
||||
update time.Time // Time of the last message query
|
||||
|
||||
lock sync.RWMutex // Lock protecting the filter internals
|
||||
} |
||||
|
||||
// newWhisperFilter creates a new serialized, poll based whisper topic filter.
|
||||
func newWhisperFilter(id int, ref *Whisper) *whisperFilter { |
||||
return &whisperFilter{ |
||||
id: id, |
||||
ref: ref, |
||||
|
||||
update: time.Now(), |
||||
skip: make(map[common.Hash]struct{}), |
||||
} |
||||
} |
||||
|
||||
// messages retrieves all the cached messages from the entire pool matching the
|
||||
// filter, resetting the filter's change buffer.
|
||||
func (w *whisperFilter) messages() []WhisperMessage { |
||||
w.lock.Lock() |
||||
defer w.lock.Unlock() |
||||
|
||||
w.cache = nil |
||||
w.update = time.Now() |
||||
|
||||
w.skip = make(map[common.Hash]struct{}) |
||||
messages := w.ref.Messages(w.id) |
||||
for _, message := range messages { |
||||
w.skip[message.ref.Hash] = struct{}{} |
||||
} |
||||
return messages |
||||
} |
||||
|
||||
// insert injects a new batch of messages into the filter cache.
|
||||
func (w *whisperFilter) insert(messages ...WhisperMessage) { |
||||
w.lock.Lock() |
||||
defer w.lock.Unlock() |
||||
|
||||
for _, message := range messages { |
||||
if _, ok := w.skip[message.ref.Hash]; !ok { |
||||
w.cache = append(w.cache, messages...) |
||||
} |
||||
} |
||||
} |
||||
|
||||
// retrieve fetches all the cached messages from the filter.
|
||||
func (w *whisperFilter) retrieve() (messages []WhisperMessage) { |
||||
w.lock.Lock() |
||||
defer w.lock.Unlock() |
||||
|
||||
messages, w.cache = w.cache, nil |
||||
w.update = time.Now() |
||||
|
||||
return |
||||
} |
||||
|
||||
// activity returns the last time instance when client requests were executed on
|
||||
// the filter.
|
||||
func (w *whisperFilter) activity() time.Time { |
||||
w.lock.RLock() |
||||
defer w.lock.RUnlock() |
||||
|
||||
return w.update |
||||
} |
@ -0,0 +1,37 @@ |
||||
// Contains the external API representation of a whisper message.
|
||||
|
||||
package xeth |
||||
|
||||
import ( |
||||
"time" |
||||
|
||||
"github.com/ethereum/go-ethereum/common" |
||||
"github.com/ethereum/go-ethereum/crypto" |
||||
"github.com/ethereum/go-ethereum/whisper" |
||||
) |
||||
|
||||
// WhisperMessage is the external API representation of a whisper.Message.
|
||||
type WhisperMessage struct { |
||||
ref *whisper.Message |
||||
|
||||
Payload string `json:"payload"` |
||||
To string `json:"to"` |
||||
From string `json:"from"` |
||||
Sent int64 `json:"sent"` |
||||
TTL int64 `json:"ttl"` |
||||
Hash string `json:"hash"` |
||||
} |
||||
|
||||
// NewWhisperMessage converts an internal message into an API version.
|
||||
func NewWhisperMessage(message *whisper.Message) WhisperMessage { |
||||
return WhisperMessage{ |
||||
ref: message, |
||||
|
||||
Payload: common.ToHex(message.Payload), |
||||
From: common.ToHex(crypto.FromECDSAPub(message.Recover())), |
||||
To: common.ToHex(crypto.FromECDSAPub(message.To)), |
||||
Sent: message.Sent.Unix(), |
||||
TTL: int64(message.TTL / time.Second), |
||||
Hash: common.ToHex(message.Hash.Bytes()), |
||||
} |
||||
} |
Loading…
Reference in new issue