Skip to content

Commit

Permalink
Merge pull request #738 from karalabe/whisper-cleanup
Browse files Browse the repository at this point in the history
Whisper cleanup, part 3
  • Loading branch information
obscuren committed Apr 28, 2015
2 parents 182d484 + 978ffd3 commit a05c420
Show file tree
Hide file tree
Showing 20 changed files with 1,024 additions and 216 deletions.
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

0 comments on commit a05c420

Please sign in to comment.