Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Whisper cleanup, part 3 #738

Merged
merged 14 commits into from
Apr 28, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions eth/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -222,10 +222,12 @@ func New(config *Config) (*Ethereum, error) {
eth.txPool = core.NewTxPool(eth.EventMux(), eth.chainManager.State, eth.chainManager.GasLimit)
eth.blockProcessor = core.NewBlockProcessor(stateDb, extraDb, eth.pow, eth.txPool, eth.chainManager, eth.EventMux())
eth.chainManager.SetProcessor(eth.blockProcessor)
eth.whisper = whisper.New()
eth.shhVersionId = int(eth.whisper.Version())
eth.miner = miner.New(eth, eth.pow, config.MinerThreads)
eth.protocolManager = NewProtocolManager(config.ProtocolVersion, config.NetworkId, eth.eventMux, eth.txPool, eth.chainManager, eth.downloader)
if config.Shh {
eth.whisper = whisper.New()
eth.shhVersionId = int(eth.whisper.Version())
}

netprv, err := config.nodeKey()
if err != nil {
Expand Down
36 changes: 19 additions & 17 deletions rpc/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -406,65 +406,67 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err

res, _ := api.xeth().DbGet([]byte(args.Database + args.Key))
*reply = newHexData(res)

case "shh_version":
// Retrieves the currently running whisper protocol version
*reply = api.xeth().WhisperVersion()

case "shh_post":
// Injects a new message into the whisper network
args := new(WhisperMessageArgs)
if err := json.Unmarshal(req.Params, &args); err != nil {
return err
}

err := api.xeth().Whisper().Post(args.Payload, args.To, args.From, args.Topics, args.Priority, args.Ttl)
if err != nil {
return err
}

*reply = true

case "shh_newIdentity":
// Creates a new whisper identity to use for sending/receiving messages
*reply = api.xeth().Whisper().NewIdentity()
// case "shh_removeIdentity":
// args := new(WhisperIdentityArgs)
// if err := json.Unmarshal(req.Params, &args); err != nil {
// return err
// }
// *reply = api.xeth().Whisper().RemoveIdentity(args.Identity)

case "shh_hasIdentity":
// Checks if an identity if owned or not
args := new(WhisperIdentityArgs)
if err := json.Unmarshal(req.Params, &args); err != nil {
return err
}
*reply = api.xeth().Whisper().HasIdentity(args.Identity)
case "shh_newGroup", "shh_addToGroup":
return NewNotImplementedError(req.Method)

case "shh_newFilter":
// Create a new filter to watch and match messages with
args := new(WhisperFilterArgs)
if err := json.Unmarshal(req.Params, &args); err != nil {
return err
}
opts := new(xeth.Options)
// opts.From = args.From
opts.To = args.To
opts.Topics = args.Topics
id := api.xeth().NewWhisperFilter(opts)
id := api.xeth().NewWhisperFilter(args.To, args.From, args.Topics)
*reply = newHexNum(big.NewInt(int64(id)).Bytes())

case "shh_uninstallFilter":
// Remove an existing filter watching messages
args := new(FilterIdArgs)
if err := json.Unmarshal(req.Params, &args); err != nil {
return err
}
*reply = api.xeth().UninstallWhisperFilter(args.Id)

case "shh_getFilterChanges":
// Retrieve all the new messages arrived since the last request
args := new(FilterIdArgs)
if err := json.Unmarshal(req.Params, &args); err != nil {
return err
}
*reply = api.xeth().MessagesChanged(args.Id)
*reply = api.xeth().WhisperMessagesChanged(args.Id)

case "shh_getMessages":
// Retrieve all the cached messages matching a specific, existing filter
args := new(FilterIdArgs)
if err := json.Unmarshal(req.Params, &args); err != nil {
return err
}
*reply = api.xeth().Whisper().Messages(args.Id)
*reply = api.xeth().WhisperMessages(args.Id)

// case "eth_register":
// // Placeholder for actual type
Expand Down
69 changes: 53 additions & 16 deletions rpc/args.go
Original file line number Diff line number Diff line change
Expand Up @@ -1010,25 +1010,27 @@ func (args *WhisperIdentityArgs) UnmarshalJSON(b []byte) (err error) {
}

type WhisperFilterArgs struct {
To string `json:"to"`
To string
From string
Topics []string
Topics [][]string
}

// UnmarshalJSON implements the json.Unmarshaler interface, invoked to convert a
// JSON message blob into a WhisperFilterArgs structure.
func (args *WhisperFilterArgs) UnmarshalJSON(b []byte) (err error) {
// Unmarshal the JSON message and sanity check
var obj []struct {
To interface{}
Topics []interface{}
To interface{} `json:"to"`
From interface{} `json:"from"`
Topics interface{} `json:"topics"`
}

if err = json.Unmarshal(b, &obj); err != nil {
if err := json.Unmarshal(b, &obj); err != nil {
return NewDecodeParamError(err.Error())
}

if len(obj) < 1 {
return NewInsufficientParamsError(len(obj), 1)
}

// Retrieve the simple data contents of the filter arguments
if obj[0].To == nil {
args.To = ""
} else {
Expand All @@ -1038,17 +1040,52 @@ func (args *WhisperFilterArgs) UnmarshalJSON(b []byte) (err error) {
}
args.To = argstr
}

t := make([]string, len(obj[0].Topics))
for i, j := range obj[0].Topics {
argstr, ok := j.(string)
if obj[0].From == nil {
args.From = ""
} else {
argstr, ok := obj[0].From.(string)
if !ok {
return NewInvalidTypeError("topics["+string(i)+"]", "is not a string")
return NewInvalidTypeError("from", "is not a string")
}
t[i] = argstr
args.From = argstr
}
// Construct the nested topic array
if obj[0].Topics != nil {
// Make sure we have an actual topic array
list, ok := obj[0].Topics.([]interface{})
if !ok {
return NewInvalidTypeError("topics", "is not an array")
}
// Iterate over each topic and handle nil, string or array
topics := make([][]string, len(list))
for idx, field := range list {
switch value := field.(type) {
case nil:
topics[idx] = []string{}

case string:
topics[idx] = []string{value}

case []interface{}:
topics[idx] = make([]string, len(value))
for i, nested := range value {
switch value := nested.(type) {
case nil:
topics[idx][i] = ""

case string:
topics[idx][i] = value

default:
return NewInvalidTypeError(fmt.Sprintf("topic[%d][%d]", idx, i), "is not a string")
}
}
default:
return NewInvalidTypeError(fmt.Sprintf("topic[%d]", idx), "not a string or array")
}
}
args.Topics = topics
}
args.Topics = t

return nil
}

Expand Down
2 changes: 1 addition & 1 deletion rpc/args_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1943,7 +1943,7 @@ func TestWhisperFilterArgs(t *testing.T) {
input := `[{"topics": ["0x68656c6c6f20776f726c64"], "to": "0x34ag445g3455b34"}]`
expected := new(WhisperFilterArgs)
expected.To = "0x34ag445g3455b34"
expected.Topics = []string{"0x68656c6c6f20776f726c64"}
expected.Topics = [][]string{[]string{"0x68656c6c6f20776f726c64"}}

args := new(WhisperFilterArgs)
if err := json.Unmarshal([]byte(input), &args); err != nil {
Expand Down
2 changes: 1 addition & 1 deletion ui/qt/qwhisper/whisper.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ func filterFromMap(opts map[string]interface{}) (f whisper.Filter) {
if topicList, ok := opts["topics"].(*qml.List); ok {
var topics []string
topicList.Convert(&topics)
f.Topics = whisper.NewTopicsFromStrings(topics...)
f.Topics = whisper.NewFilterTopicsFromStringsFlat(topics...)
}

return
Expand Down
3 changes: 3 additions & 0 deletions whisper/envelope.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@ func (self *Envelope) Open(key *ecdsa.PrivateKey) (msg *Message, err error) {

message := &Message{
Flags: data[0],
Sent: time.Unix(int64(self.Expiry-self.TTL), 0),
TTL: time.Duration(self.TTL) * time.Second,
Hash: self.Hash(),
}
data = data[1:]

Expand Down
142 changes: 142 additions & 0 deletions whisper/envelope_test.go
Original file line number Diff line number Diff line change
@@ -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)
}
}
Loading