From c6125266a12260d58976ad833e8bf4bc24975e1d Mon Sep 17 00:00:00 2001 From: Vlad Date: Wed, 15 Feb 2017 22:35:44 +0100 Subject: [PATCH 01/12] whisper: wnode updated for tests with geth --- cmd/wnode/main.go | 48 ++++++++++++++++++----------------------------- 1 file changed, 18 insertions(+), 30 deletions(-) diff --git a/cmd/wnode/main.go b/cmd/wnode/main.go index 175021798a..28e93e3222 100644 --- a/cmd/wnode/main.go +++ b/cmd/wnode/main.go @@ -22,8 +22,6 @@ package main import ( "bufio" "crypto/ecdsa" - "crypto/sha1" - "crypto/sha256" "crypto/sha512" "encoding/binary" "encoding/hex" @@ -49,6 +47,7 @@ import ( ) const quitCommand = "~Q" +const symKeyName = "da919ea33001b04dfc630522e33078ec0df11" // singletons var ( @@ -68,6 +67,7 @@ var ( nodeid *ecdsa.PrivateKey topic whisper.TopicType filterID uint32 + symPass string msPassword string ) @@ -88,7 +88,6 @@ var ( argServerPoW = flag.Float64("mspow", whisper.MinimumPoW, "PoW requirement for Mail Server request") argIP = flag.String("ip", "", "IP address and port of this node (e.g. 127.0.0.1:30303)") - argSalt = flag.String("salt", "", "salt (for topic and key derivation)") argPub = flag.String("pub", "", "public key for asymmetric encryption") argDBPath = flag.String("dbpath", "", "path to the server's DB directory") argIDFile = flag.String("idfile", "", "file name with node id (private key)") @@ -146,7 +145,6 @@ func echo() { fmt.Printf("pow = %f \n", *argPoW) fmt.Printf("mspow = %f \n", *argServerPoW) fmt.Printf("ip = %s \n", *argIP) - fmt.Printf("salt = %s \n", *argSalt) fmt.Printf("pub = %s \n", common.ToHex(crypto.FromECDSAPub(pub))) fmt.Printf("idfile = %s \n", *argIDFile) fmt.Printf("dbpath = %s \n", *argDBPath) @@ -172,10 +170,7 @@ func initialize() { } if *testMode { - password := []byte("test password for symmetric encryption") - salt := []byte("test salt for symmetric encryption") - symKey = pbkdf2.Key(password, salt, 64, 32, sha256.New) - topic = whisper.TopicType{0xFF, 0xFF, 0xFF, 0xFF} + symPass = "test" msPassword = "mail server test password" } @@ -286,20 +281,18 @@ func configureNode() { } } - if !*asymmetricMode && !*forwarderMode && !*testMode { - pass, err := console.Stdin.PromptPassword("Please enter the password: ") - if err != nil { - utils.Fatalf("Failed to read passphrase: %v", err) + if !*asymmetricMode && !*forwarderMode { + if len(symPass) == 0 { + symPass, err = console.Stdin.PromptPassword("Please enter the password: ") + if err != nil { + utils.Fatalf("Failed to read passphrase: %v", err) + } } - if len(*argSalt) == 0 { - argSalt = scanLineA("Please enter the salt: ") - } - - symKey = pbkdf2.Key([]byte(pass), []byte(*argSalt), 65356, 32, sha256.New) - + shh.AddSymKey(symKeyName, []byte(symPass)) + symKey = shh.GetSymKey(symKeyName) if len(*argTopic) == 0 { - generateTopic([]byte(pass), []byte(*argSalt)) + generateTopic([]byte(symPass)) } } @@ -319,15 +312,10 @@ func configureNode() { fmt.Printf("Filter is configured for the topic: %x \n", topic) } -func generateTopic(password, salt []byte) { - const rounds = 4000 - const size = 128 - x1 := pbkdf2.Key(password, salt, rounds, size, sha512.New) - x2 := pbkdf2.Key(password, salt, rounds, size, sha1.New) - x3 := pbkdf2.Key(x1, x2, rounds, size, sha256.New) - - for i := 0; i < size; i++ { - topic[i%whisper.TopicLength] ^= x3[i] +func generateTopic(password []byte) { + x := pbkdf2.Key(password, password, 8196, 128, sha512.New) + for i := 0; i < len(x); i++ { + topic[i%whisper.TopicLength] ^= x[i] } } @@ -379,9 +367,9 @@ func sendLoop() { if *asymmetricMode { // print your own message for convenience, // because in asymmetric mode it is impossible to decrypt it - hour, min, sec := time.Now().Clock() + timestamp := time.Now().Unix() from := crypto.PubkeyToAddress(asymKey.PublicKey) - fmt.Printf("\n%02d:%02d:%02d <%x>: %s\n", hour, min, sec, from, s) + fmt.Printf("\n%d <%x>: %s\n", timestamp, from, s) } } } From 15fc6eab073909179b5c00da9c61ca5a70fab161 Mon Sep 17 00:00:00 2001 From: Vlad Date: Thu, 16 Feb 2017 10:04:55 +0100 Subject: [PATCH 02/12] whisper: updated processing of incoming messages --- whisper/whisperv5/api.go | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/whisper/whisperv5/api.go b/whisper/whisperv5/api.go index ce41624dfe..9f28c97326 100644 --- a/whisper/whisperv5/api.go +++ b/whisper/whisperv5/api.go @@ -225,7 +225,7 @@ func (api *PublicWhisperAPI) UninstallFilter(filterId uint32) { } // GetFilterChanges retrieves all the new messages matched by a filter since the last retrieval. -func (api *PublicWhisperAPI) GetFilterChanges(filterId uint32) []WhisperMessage { +func (api *PublicWhisperAPI) GetFilterChanges(filterId uint32) []*WhisperMessage { f := api.whisper.GetFilter(filterId) if f != nil { newMail := f.Retrieve() @@ -235,14 +235,14 @@ func (api *PublicWhisperAPI) GetFilterChanges(filterId uint32) []WhisperMessage } // GetMessages retrieves all the known messages that match a specific filter. -func (api *PublicWhisperAPI) GetMessages(filterId uint32) []WhisperMessage { +func (api *PublicWhisperAPI) GetMessages(filterId uint32) []*WhisperMessage { all := api.whisper.Messages(filterId) return toWhisperMessages(all) } // toWhisperMessages converts a Whisper message to a RPC whisper message. -func toWhisperMessages(messages []*ReceivedMessage) []WhisperMessage { - msgs := make([]WhisperMessage, len(messages)) +func toWhisperMessages(messages []*ReceivedMessage) []*WhisperMessage { + msgs := make([]*WhisperMessage, len(messages)) for i, msg := range messages { msgs[i] = NewWhisperMessage(msg) } @@ -449,15 +449,21 @@ type WhisperMessage struct { } // NewWhisperMessage converts an internal message into an API version. -func NewWhisperMessage(message *ReceivedMessage) WhisperMessage { - return WhisperMessage{ +func NewWhisperMessage(message *ReceivedMessage) *WhisperMessage { + msg := WhisperMessage{ Payload: common.ToHex(message.Payload), Padding: common.ToHex(message.Padding), - From: common.ToHex(crypto.FromECDSAPub(message.SigToPubKey())), - To: common.ToHex(crypto.FromECDSAPub(message.Dst)), Sent: message.Sent, TTL: message.TTL, PoW: message.PoW, Hash: common.ToHex(message.EnvelopeHash.Bytes()), } + + if message.Dst != nil { + msg.To = common.ToHex(crypto.FromECDSAPub(message.Dst)) + } + if isMessageSigned(message.Raw[0]) { + msg.From = common.ToHex(crypto.FromECDSAPub(message.SigToPubKey())) + } + return &msg } From 545e121f222020ad74cf64e88c6f4df29ebf3f1f Mon Sep 17 00:00:00 2001 From: Vlad Date: Thu, 16 Feb 2017 13:36:02 +0100 Subject: [PATCH 03/12] whisper: symmetric encryption updated --- cmd/wnode/main.go | 2 +- whisper/whisperv5/api.go | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/cmd/wnode/main.go b/cmd/wnode/main.go index 28e93e3222..89e862c29c 100644 --- a/cmd/wnode/main.go +++ b/cmd/wnode/main.go @@ -170,7 +170,7 @@ func initialize() { } if *testMode { - symPass = "test" + symPass = "wwww" // ascii code: 0x77777777 msPassword = "mail server test password" } diff --git a/whisper/whisperv5/api.go b/whisper/whisperv5/api.go index 9f28c97326..115f642736 100644 --- a/whisper/whisperv5/api.go +++ b/whisper/whisperv5/api.go @@ -123,7 +123,7 @@ func (api *PublicWhisperAPI) GenerateSymKey(name string) error { } // AddSymKey stores the key under the 'name' id. -func (api *PublicWhisperAPI) AddSymKey(name string, key []byte) error { +func (api *PublicWhisperAPI) AddSymKey(name string, key hexutil.Bytes) error { if api.whisper == nil { return whisperOffLineErr } @@ -438,6 +438,7 @@ func (args *WhisperFilterArgs) UnmarshalJSON(b []byte) (err error) { // WhisperMessage is the RPC representation of a whisper message. type WhisperMessage struct { + Topic string `json:"topic"` Payload string `json:"payload"` Padding string `json:"padding"` From string `json:"from"` @@ -451,6 +452,7 @@ type WhisperMessage struct { // NewWhisperMessage converts an internal message into an API version. func NewWhisperMessage(message *ReceivedMessage) *WhisperMessage { msg := WhisperMessage{ + Topic: common.ToHex(message.Topic[:]), Payload: common.ToHex(message.Payload), Padding: common.ToHex(message.Padding), Sent: message.Sent, From dc63edcef0c8a0a80b7de40fba3bbe9b609868a2 Mon Sep 17 00:00:00 2001 From: Vlad Date: Thu, 16 Feb 2017 17:58:09 +0100 Subject: [PATCH 04/12] whisper: filter id type changed to enhance security --- cmd/wnode/main.go | 2 +- whisper/whisperv5/api.go | 28 +++++++++--------- whisper/whisperv5/api_test.go | 8 +++--- whisper/whisperv5/filter.go | 47 ++++++++++++++++++++++++------- whisper/whisperv5/filter_test.go | 27 ++++++++++-------- whisper/whisperv5/peer_test.go | 2 +- whisper/whisperv5/whisper.go | 8 +++--- whisper/whisperv5/whisper_test.go | 4 +-- 8 files changed, 78 insertions(+), 48 deletions(-) diff --git a/cmd/wnode/main.go b/cmd/wnode/main.go index 89e862c29c..9a9bd4b440 100644 --- a/cmd/wnode/main.go +++ b/cmd/wnode/main.go @@ -66,7 +66,7 @@ var ( asymKey *ecdsa.PrivateKey nodeid *ecdsa.PrivateKey topic whisper.TopicType - filterID uint32 + filterID string symPass string msPassword string ) diff --git a/whisper/whisperv5/api.go b/whisper/whisperv5/api.go index 115f642736..b3f441303b 100644 --- a/whisper/whisperv5/api.go +++ b/whisper/whisperv5/api.go @@ -151,9 +151,9 @@ func (api *PublicWhisperAPI) DeleteSymKey(name string) error { // NewWhisperFilter creates and registers a new message filter to watch for inbound whisper messages. // Returns the ID of the newly created Filter. -func (api *PublicWhisperAPI) NewFilter(args WhisperFilterArgs) (uint32, error) { +func (api *PublicWhisperAPI) NewFilter(args WhisperFilterArgs) (string, error) { if api.whisper == nil { - return 0, whisperOffLineErr + return "", whisperOffLineErr } filter := Filter{ @@ -171,25 +171,25 @@ func (api *PublicWhisperAPI) NewFilter(args WhisperFilterArgs) (uint32, error) { if len(args.Topics) == 0 { info := "NewFilter: at least one topic must be specified" glog.V(logger.Error).Infof(info) - return 0, errors.New(info) + return "", errors.New(info) } if len(args.KeyName) != 0 && len(filter.KeySym) == 0 { info := "NewFilter: key was not found by name: " + args.KeyName glog.V(logger.Error).Infof(info) - return 0, errors.New(info) + return "", errors.New(info) } if len(args.To) == 0 && len(filter.KeySym) == 0 { info := "NewFilter: filter must contain either symmetric or asymmetric key" glog.V(logger.Error).Infof(info) - return 0, errors.New(info) + return "", errors.New(info) } if len(args.To) != 0 && len(filter.KeySym) != 0 { info := "NewFilter: filter must not contain both symmetric and asymmetric key" glog.V(logger.Error).Infof(info) - return 0, errors.New(info) + return "", errors.New(info) } if len(args.To) > 0 { @@ -197,13 +197,13 @@ func (api *PublicWhisperAPI) NewFilter(args WhisperFilterArgs) (uint32, error) { if !ValidatePublicKey(dst) { info := "NewFilter: Invalid 'To' address" glog.V(logger.Error).Infof(info) - return 0, errors.New(info) + return "", errors.New(info) } filter.KeyAsym = api.whisper.GetIdentity(string(args.To)) if filter.KeyAsym == nil { info := "NewFilter: non-existent identity provided" glog.V(logger.Error).Infof(info) - return 0, errors.New(info) + return "", errors.New(info) } } @@ -211,7 +211,7 @@ func (api *PublicWhisperAPI) NewFilter(args WhisperFilterArgs) (uint32, error) { if !ValidatePublicKey(filter.Src) { info := "NewFilter: Invalid 'From' address" glog.V(logger.Error).Infof(info) - return 0, errors.New(info) + return "", errors.New(info) } } @@ -220,12 +220,12 @@ func (api *PublicWhisperAPI) NewFilter(args WhisperFilterArgs) (uint32, error) { } // UninstallFilter disables and removes an existing filter. -func (api *PublicWhisperAPI) UninstallFilter(filterId uint32) { +func (api *PublicWhisperAPI) UninstallFilter(filterId string) { api.whisper.Unwatch(filterId) } // GetFilterChanges retrieves all the new messages matched by a filter since the last retrieval. -func (api *PublicWhisperAPI) GetFilterChanges(filterId uint32) []*WhisperMessage { +func (api *PublicWhisperAPI) GetFilterChanges(filterId string) []*WhisperMessage { f := api.whisper.GetFilter(filterId) if f != nil { newMail := f.Retrieve() @@ -235,7 +235,7 @@ func (api *PublicWhisperAPI) GetFilterChanges(filterId uint32) []*WhisperMessage } // GetMessages retrieves all the known messages that match a specific filter. -func (api *PublicWhisperAPI) GetMessages(filterId uint32) []*WhisperMessage { +func (api *PublicWhisperAPI) GetMessages(filterId string) []*WhisperMessage { all := api.whisper.Messages(filterId) return toWhisperMessages(all) } @@ -282,7 +282,7 @@ func (api *PublicWhisperAPI) Post(args PostArgs) error { } filter := api.whisper.GetFilter(args.FilterID) - if filter == nil && args.FilterID > 0 { + if filter == nil && len(args.FilterID) > 0 { info := fmt.Sprintf("Post: wrong filter id %d", args.FilterID) glog.V(logger.Error).Infof(info) return errors.New(info) @@ -374,7 +374,7 @@ type PostArgs struct { Payload hexutil.Bytes `json:"payload"` WorkTime uint32 `json:"worktime"` PoW float64 `json:"pow"` - FilterID uint32 `json:"filterID"` + FilterID string `json:"filterID"` PeerID hexutil.Bytes `json:"peerID"` } diff --git a/whisper/whisperv5/api_test.go b/whisper/whisperv5/api_test.go index f7deab39c4..79285559f3 100644 --- a/whisper/whisperv5/api_test.go +++ b/whisper/whisperv5/api_test.go @@ -42,7 +42,7 @@ func TestBasic(t *testing.T) { t.Fatalf("wrong version: %d.", ver) } - mail := api.GetFilterChanges(1) + mail := api.GetFilterChanges("non-existent-id") if len(mail) != 0 { t.Fatalf("failed GetFilterChanges: premature result") } @@ -212,7 +212,7 @@ func TestUnmarshalPostArgs(t *testing.T) { "payload":"0x7061796C6F61642073686F756C642062652070736575646F72616E646F6D", "worktime":777, "pow":3.1416, - "filterID":64, + "filterID":"test-filter-id", "peerID":"0xf26e7779" }`) @@ -249,7 +249,7 @@ func TestUnmarshalPostArgs(t *testing.T) { if a.PoW != 3.1416 { t.Fatalf("wrong pow: %f.", a.PoW) } - if a.FilterID != 64 { + if a.FilterID != "test-filter-id" { t.Fatalf("wrong FilterID: %d.", a.FilterID) } if !bytes.Equal(a.PeerID[:], a.Topic[:]) { @@ -257,7 +257,7 @@ func TestUnmarshalPostArgs(t *testing.T) { } } -func waitForMessage(api *PublicWhisperAPI, id uint32, target int) bool { +func waitForMessage(api *PublicWhisperAPI, id string, target int) bool { for i := 0; i < 64; i++ { all := api.GetMessages(id) if len(all) >= target { diff --git a/whisper/whisperv5/filter.go b/whisper/whisperv5/filter.go index a386aa1646..8dd1f04a77 100644 --- a/whisper/whisperv5/filter.go +++ b/whisper/whisperv5/filter.go @@ -18,6 +18,8 @@ package whisperv5 import ( "crypto/ecdsa" + crand "crypto/rand" + "fmt" "sync" "github.com/ethereum/go-ethereum/common" @@ -39,20 +41,45 @@ type Filter struct { } type Filters struct { - id uint32 // can contain any value except zero - watchers map[uint32]*Filter + watchers map[string]*Filter whisper *Whisper mutex sync.RWMutex } func NewFilters(w *Whisper) *Filters { return &Filters{ - watchers: make(map[uint32]*Filter), + watchers: make(map[string]*Filter), whisper: w, } } -func (fs *Filters) Install(watcher *Filter) uint32 { +func (fs *Filters) generateRandomID() (id string) { + var err error + buf := make([]byte, 20) + for i := 0; i < 3; i++ { + _, err = crand.Read(buf) + if err != nil { + continue + } + if !validateSymmetricKey(buf) { + err = fmt.Errorf("error in generateRandomID: crypto/rand failed to generate random data") + continue + } + id = common.Bytes2Hex(buf) + if fs.watchers[id] != nil { + err = fmt.Errorf("error in generateRandomID: generated same ID twice") + continue + } + return id + } + + // at this point all the attempts to generate the random ID failed. + // this poses a serious danger, since whisper very much relies + // on random numbers. the program must be terminated. + panic(err) +} + +func (fs *Filters) Install(watcher *Filter) string { if watcher.Messages == nil { watcher.Messages = make(map[common.Hash]*ReceivedMessage) } @@ -60,21 +87,21 @@ func (fs *Filters) Install(watcher *Filter) uint32 { fs.mutex.Lock() defer fs.mutex.Unlock() - fs.id++ - fs.watchers[fs.id] = watcher - return fs.id + id := fs.generateRandomID() + fs.watchers[id] = watcher + return id } -func (fs *Filters) Uninstall(id uint32) { +func (fs *Filters) Uninstall(id string) { fs.mutex.Lock() defer fs.mutex.Unlock() delete(fs.watchers, id) } -func (fs *Filters) Get(i uint32) *Filter { +func (fs *Filters) Get(id string) *Filter { fs.mutex.RLock() defer fs.mutex.RUnlock() - return fs.watchers[i] + return fs.watchers[id] } func (fs *Filters) NotifyWatchers(env *Envelope, p2pMessage bool) { diff --git a/whisper/whisperv5/filter_test.go b/whisper/whisperv5/filter_test.go index 5bf607cccf..259b22a0d7 100644 --- a/whisper/whisperv5/filter_test.go +++ b/whisper/whisperv5/filter_test.go @@ -43,7 +43,7 @@ func InitDebugTest(i int64) { type FilterTestCase struct { f *Filter - id uint32 + id string alive bool msgCnt int } @@ -100,14 +100,13 @@ func TestInstallFilters(t *testing.T) { filters := NewFilters(w) tst := generateTestCases(t, SizeTestFilters) - var j uint32 + var j string for i := 0; i < SizeTestFilters; i++ { j = filters.Install(tst[i].f) tst[i].id = j - } - - if j < SizeTestFilters-1 { - t.Fatalf("seed %d: wrong index %d", seed, j) + if len(j) != 40 { + t.Fatalf("seed %d: wrong filter id size [%d]", seed, len(j)) + } } for _, testCase := range tst { @@ -519,17 +518,21 @@ func TestWatchers(t *testing.T) { var i int var j uint32 var e *Envelope + var x, firstID string w := New() filters := NewFilters(w) tst := generateTestCases(t, NumFilters) for i = 0; i < NumFilters; i++ { tst[i].f.Src = nil - j = filters.Install(tst[i].f) - tst[i].id = j + x = filters.Install(tst[i].f) + tst[i].id = x + if len(firstID) == 0 { + firstID = x + } } - last := j + lastID := x var envelopes [NumMessages]*Envelope for i = 0; i < NumMessages; i++ { @@ -571,9 +574,9 @@ func TestWatchers(t *testing.T) { // another round with a cloned filter clone := cloneFilter(tst[0].f) - filters.Uninstall(last) + filters.Uninstall(lastID) total = 0 - last = NumFilters - 1 + last := NumFilters - 1 tst[last].f = clone filters.Install(clone) for i = 0; i < NumFilters; i++ { @@ -640,7 +643,7 @@ func TestWatchers(t *testing.T) { t.Fatalf("failed with seed %d: total: got %d, want 0.", seed, total) } - f := filters.Get(1) + f := filters.Get(firstID) if f == nil { t.Fatalf("failed to get the filter with seed %d.", seed) } diff --git a/whisper/whisperv5/peer_test.go b/whisper/whisperv5/peer_test.go index cc98ba2d67..fc4f29ceef 100644 --- a/whisper/whisperv5/peer_test.go +++ b/whisper/whisperv5/peer_test.go @@ -79,7 +79,7 @@ type TestNode struct { shh *Whisper id *ecdsa.PrivateKey server *p2p.Server - filerId uint32 + filerId string } var result TestData diff --git a/whisper/whisperv5/whisper.go b/whisper/whisperv5/whisper.go index a027fd84b3..87552b15ef 100644 --- a/whisper/whisperv5/whisper.go +++ b/whisper/whisperv5/whisper.go @@ -272,16 +272,16 @@ func (w *Whisper) GetSymKey(name string) []byte { // Watch installs a new message handler to run in case a matching packet arrives // from the whisper network. -func (w *Whisper) Watch(f *Filter) uint32 { +func (w *Whisper) Watch(f *Filter) string { return w.filters.Install(f) } -func (w *Whisper) GetFilter(id uint32) *Filter { +func (w *Whisper) GetFilter(id string) *Filter { return w.filters.Get(id) } // Unwatch removes an installed message handler. -func (w *Whisper) Unwatch(id uint32) { +func (w *Whisper) Unwatch(id string) { w.filters.Uninstall(id) } @@ -575,7 +575,7 @@ func (w *Whisper) Envelopes() []*Envelope { } // Messages retrieves all the decrypted messages matching a filter id. -func (w *Whisper) Messages(id uint32) []*ReceivedMessage { +func (w *Whisper) Messages(id string) []*ReceivedMessage { result := make([]*ReceivedMessage, 0) w.poolMu.RLock() defer w.poolMu.RUnlock() diff --git a/whisper/whisperv5/whisper_test.go b/whisper/whisperv5/whisper_test.go index c2ae35a3ee..312dacfc4b 100644 --- a/whisper/whisperv5/whisper_test.go +++ b/whisper/whisperv5/whisper_test.go @@ -44,7 +44,7 @@ func TestWhisperBasic(t *testing.T) { if uint64(w.Version()) != ProtocolVersion { t.Fatalf("failed whisper Version: %v.", shh.Version) } - if w.GetFilter(0) != nil { + if w.GetFilter("non-existent") != nil { t.Fatalf("failed GetFilter.") } @@ -69,7 +69,7 @@ func TestWhisperBasic(t *testing.T) { if len(mail) != 0 { t.Fatalf("failed w.Envelopes().") } - m := w.Messages(0) + m := w.Messages("non-existent") if len(m) != 0 { t.Fatalf("failed w.Messages.") } From e85268fcd184d4af58416bbac06c432a940069bc Mon Sep 17 00:00:00 2001 From: Vlad Date: Sat, 18 Feb 2017 18:06:54 +0100 Subject: [PATCH 05/12] whisper: allow filter without topic for asymmetric encryption --- whisper/whisperv5/api.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/whisper/whisperv5/api.go b/whisper/whisperv5/api.go index b3f441303b..5b5b649a49 100644 --- a/whisper/whisperv5/api.go +++ b/whisper/whisperv5/api.go @@ -168,7 +168,7 @@ func (api *PublicWhisperAPI) NewFilter(args WhisperFilterArgs) (string, error) { } filter.Topics = append(filter.Topics, args.Topics...) - if len(args.Topics) == 0 { + if len(args.Topics) == 0 && len(args.KeyName) != 0 { info := "NewFilter: at least one topic must be specified" glog.V(logger.Error).Infof(info) return "", errors.New(info) @@ -374,17 +374,17 @@ type PostArgs struct { Payload hexutil.Bytes `json:"payload"` WorkTime uint32 `json:"worktime"` PoW float64 `json:"pow"` - FilterID string `json:"filterID"` - PeerID hexutil.Bytes `json:"peerID"` + FilterID string `json:"filterid"` + PeerID hexutil.Bytes `json:"peerid"` } type WhisperFilterArgs struct { - To string - From string - KeyName string - PoW float64 - Topics []TopicType - AcceptP2P bool + To string `json:"to"` + From string `json:"from"` + KeyName string `json:"keyname"` + PoW float64 `json:"pow"` + Topics []TopicType `json:"topics"` + AcceptP2P bool `json:"p2p"` } // UnmarshalJSON implements the json.Unmarshaler interface, invoked to convert a @@ -397,7 +397,7 @@ func (args *WhisperFilterArgs) UnmarshalJSON(b []byte) (err error) { KeyName string `json:"keyname"` PoW float64 `json:"pow"` Topics []interface{} `json:"topics"` - AcceptP2P bool `json:"acceptP2P"` + AcceptP2P bool `json:"p2p"` } if err := json.Unmarshal(b, &obj); err != nil { return err From 9b119887e4bed24090cd7e95ccd657b5c97d4079 Mon Sep 17 00:00:00 2001 From: Vlad Date: Sat, 18 Feb 2017 20:21:13 +0100 Subject: [PATCH 06/12] whisper: POW updated --- whisper/whisperv5/api_test.go | 6 +++--- whisper/whisperv5/benchmarks_test.go | 31 +++++++++++++++++++++++++++- whisper/whisperv5/doc.go | 4 ++-- whisper/whisperv5/envelope.go | 8 +++++-- 4 files changed, 41 insertions(+), 8 deletions(-) diff --git a/whisper/whisperv5/api_test.go b/whisper/whisperv5/api_test.go index 79285559f3..48536b84ba 100644 --- a/whisper/whisperv5/api_test.go +++ b/whisper/whisperv5/api_test.go @@ -152,7 +152,7 @@ func TestUnmarshalFilterArgs(t *testing.T) { "keyname":"testname", "pow":2.34, "topics":["0x00000000", "0x007f80ff", "0xff807f00", "0xf26e7779"], - "acceptP2P":true + "p2p":true }`) var f WhisperFilterArgs @@ -212,8 +212,8 @@ func TestUnmarshalPostArgs(t *testing.T) { "payload":"0x7061796C6F61642073686F756C642062652070736575646F72616E646F6D", "worktime":777, "pow":3.1416, - "filterID":"test-filter-id", - "peerID":"0xf26e7779" + "filterid":"test-filter-id", + "peerid":"0xf26e7779" }`) var a PostArgs diff --git a/whisper/whisperv5/benchmarks_test.go b/whisper/whisperv5/benchmarks_test.go index f2eef3c471..417b2881b8 100644 --- a/whisper/whisperv5/benchmarks_test.go +++ b/whisper/whisperv5/benchmarks_test.go @@ -34,7 +34,6 @@ func BenchmarkDeriveOneTimeKey(b *testing.B) { } } -//func TestEncryptionSym(b *testing.T) { func BenchmarkEncryptionSym(b *testing.B) { InitSingleTest() @@ -181,3 +180,33 @@ func BenchmarkDecryptionAsymInvalid(b *testing.B) { } } } + +func increment(x []byte) { + for i := 0; i < len(x); i++ { + x[i]++ + if x[i] != 0 { + break + } + } +} + +func BenchmarkPoW(b *testing.B) { + InitSingleTest() + + params, err := generateMessageParams() + if err != nil { + b.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) + } + params.Payload = make([]byte, 32) + params.PoW = 10.0 + params.TTL = 1 + + for i := 0; i < b.N; i++ { + increment(params.Payload) + msg := NewSentMessage(params) + _, err := msg.Wrap(params) + if err != nil { + b.Fatalf("failed Wrap with seed %d: %s.", seed, err) + } + } +} diff --git a/whisper/whisperv5/doc.go b/whisper/whisperv5/doc.go index b82a82468b..70c7008a73 100644 --- a/whisper/whisperv5/doc.go +++ b/whisper/whisperv5/doc.go @@ -55,8 +55,8 @@ const ( saltLength = 12 AESNonceMaxLength = 12 - MaxMessageLength = 0xFFFF // todo: remove this restriction after testing. this should be regulated by PoW. - MinimumPoW = 1.0 // todo: review after testing. + MaxMessageLength = 0x0FFFFF // todo: remove this restriction after testing. this should be regulated by PoW. + MinimumPoW = 10.0 // todo: review after testing. padSizeLimitLower = 128 // it can not be less - we don't want to reveal the absence of signature padSizeLimitUpper = 256 // just an arbitrary number, could be changed without losing compatibility diff --git a/whisper/whisperv5/envelope.go b/whisper/whisperv5/envelope.go index 1b976705d4..c89238b7a2 100644 --- a/whisper/whisperv5/envelope.go +++ b/whisper/whisperv5/envelope.go @@ -122,6 +122,10 @@ func (e *Envelope) Seal(options *MessageParams) error { return nil } +func (e *Envelope) size() int { + return len(e.Data) + len(e.Version) + len(e.AESNonce) + len(e.Salt) + 20 +} + func (e *Envelope) PoW() float64 { if e.pow == 0 { e.calculatePoW(0) @@ -137,14 +141,14 @@ func (e *Envelope) calculatePoW(diff uint32) { h = crypto.Keccak256(buf) firstBit := common.FirstBitSet(common.BigD(h)) x := math.Pow(2, float64(firstBit)) - x /= float64(len(e.Data)) // we only count e.Data, other variable-sized members are checked in Whisper.add() + x /= float64(e.size()) x /= float64(e.TTL + diff) e.pow = x } func (e *Envelope) powToFirstBit(pow float64) int { x := pow - x *= float64(len(e.Data)) + x *= float64(e.size()) x *= float64(e.TTL) bits := math.Log2(x) bits = math.Ceil(bits) From d8caedcdf338b4e6cef00ea63c91952b530d6017 Mon Sep 17 00:00:00 2001 From: Vlad Date: Sat, 18 Feb 2017 21:28:04 +0100 Subject: [PATCH 07/12] whisper: logging updated --- cmd/wnode/main.go | 3 ++- whisper/whisperv5/envelope.go | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/cmd/wnode/main.go b/cmd/wnode/main.go index 9a9bd4b440..4fd4df8f20 100644 --- a/cmd/wnode/main.go +++ b/cmd/wnode/main.go @@ -82,6 +82,7 @@ var ( testMode = flag.Bool("t", false, "use of predefined parameters for diagnostics") generateKey = flag.Bool("k", false, "generate and show the private key") + argVerbosity = flag.Int("verbosity", logger.Warn, "log verbosity level") argTTL = flag.Uint("ttl", 30, "time-to-live for messages in seconds") argWorkTime = flag.Uint("work", 5, "work time in seconds") argPoW = flag.Float64("pow", whisper.MinimumPoW, "PoW for normal messages in float format (e.g. 2.7)") @@ -152,7 +153,7 @@ func echo() { } func initialize() { - glog.SetV(logger.Warn) + glog.SetV(*argVerbosity) glog.SetToStderr(true) done = make(chan struct{}) diff --git a/whisper/whisperv5/envelope.go b/whisper/whisperv5/envelope.go index c89238b7a2..8812ae207f 100644 --- a/whisper/whisperv5/envelope.go +++ b/whisper/whisperv5/envelope.go @@ -116,7 +116,7 @@ func (e *Envelope) Seal(options *MessageParams) error { } if target > 0 && bestBit < target { - return errors.New("Failed to reach the PoW target") + return errors.New("Failed to reach the PoW target, insufficient work time") } return nil From 2ac62c3857d721bc4fdaacfcc050318e21ae399c Mon Sep 17 00:00:00 2001 From: Vlad Date: Sat, 18 Feb 2017 22:59:45 +0100 Subject: [PATCH 08/12] whisper: spellchecker update --- whisper/whisperv5/api.go | 4 ++-- whisper/whisperv5/api_test.go | 2 +- whisper/whisperv5/peer_test.go | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/whisper/whisperv5/api.go b/whisper/whisperv5/api.go index 5b5b649a49..d31cb64425 100644 --- a/whisper/whisperv5/api.go +++ b/whisper/whisperv5/api.go @@ -283,7 +283,7 @@ func (api *PublicWhisperAPI) Post(args PostArgs) error { filter := api.whisper.GetFilter(args.FilterID) if filter == nil && len(args.FilterID) > 0 { - info := fmt.Sprintf("Post: wrong filter id %d", args.FilterID) + info := fmt.Sprintf("Post: wrong filter id %s", args.FilterID) glog.V(logger.Error).Infof(info) return errors.New(info) } @@ -299,7 +299,7 @@ func (api *PublicWhisperAPI) Post(args PostArgs) error { if (params.Topic == TopicType{}) { sz := len(filter.Topics) if sz < 1 { - info := fmt.Sprintf("Post: no topics in filter # %d", args.FilterID) + info := fmt.Sprintf("Post: no topics in filter # %s", args.FilterID) glog.V(logger.Error).Infof(info) return errors.New(info) } else if sz == 1 { diff --git a/whisper/whisperv5/api_test.go b/whisper/whisperv5/api_test.go index 48536b84ba..ea0a2c40bc 100644 --- a/whisper/whisperv5/api_test.go +++ b/whisper/whisperv5/api_test.go @@ -250,7 +250,7 @@ func TestUnmarshalPostArgs(t *testing.T) { t.Fatalf("wrong pow: %f.", a.PoW) } if a.FilterID != "test-filter-id" { - t.Fatalf("wrong FilterID: %d.", a.FilterID) + t.Fatalf("wrong FilterID: %s.", a.FilterID) } if !bytes.Equal(a.PeerID[:], a.Topic[:]) { t.Fatalf("wrong PeerID: %x.", a.PeerID) diff --git a/whisper/whisperv5/peer_test.go b/whisper/whisperv5/peer_test.go index fc4f29ceef..223dd87620 100644 --- a/whisper/whisperv5/peer_test.go +++ b/whisper/whisperv5/peer_test.go @@ -187,7 +187,7 @@ func checkPropagation(t *testing.T) { for i := 0; i < NumNodes; i++ { f := nodes[i].shh.GetFilter(nodes[i].filerId) if f == nil { - t.Fatalf("failed to get filterId %d from node %d.", nodes[i].filerId, i) + t.Fatalf("failed to get filterId %s from node %d.", nodes[i].filerId, i) } mail := f.Retrieve() From d70a67abd1644101a7a8f27c85c49cab0e1046dd Mon Sep 17 00:00:00 2001 From: Vlad Date: Mon, 20 Feb 2017 17:24:29 +0100 Subject: [PATCH 09/12] whisper: error handling changed --- cmd/wnode/main.go | 5 ++++- whisper/whisperv5/api.go | 3 +-- whisper/whisperv5/filter.go | 20 +++++++++----------- whisper/whisperv5/filter_test.go | 12 ++++++++++-- whisper/whisperv5/peer_test.go | 5 ++++- whisper/whisperv5/whisper.go | 2 +- 6 files changed, 29 insertions(+), 18 deletions(-) diff --git a/cmd/wnode/main.go b/cmd/wnode/main.go index 4fd4df8f20..d002497fbf 100644 --- a/cmd/wnode/main.go +++ b/cmd/wnode/main.go @@ -309,7 +309,10 @@ func configureNode() { Topics: []whisper.TopicType{topic}, AcceptP2P: p2pAccept, } - filterID = shh.Watch(&filter) + filterID, err = shh.Watch(&filter) + if err != nil { + utils.Fatalf("Failed to install filter: %s", err) + } fmt.Printf("Filter is configured for the topic: %x \n", topic) } diff --git a/whisper/whisperv5/api.go b/whisper/whisperv5/api.go index d31cb64425..606d24c46f 100644 --- a/whisper/whisperv5/api.go +++ b/whisper/whisperv5/api.go @@ -215,8 +215,7 @@ func (api *PublicWhisperAPI) NewFilter(args WhisperFilterArgs) (string, error) { } } - id := api.whisper.Watch(&filter) - return id, nil + return api.whisper.Watch(&filter) } // UninstallFilter disables and removes an existing filter. diff --git a/whisper/whisperv5/filter.go b/whisper/whisperv5/filter.go index 8dd1f04a77..832ebe3f6b 100644 --- a/whisper/whisperv5/filter.go +++ b/whisper/whisperv5/filter.go @@ -53,8 +53,7 @@ func NewFilters(w *Whisper) *Filters { } } -func (fs *Filters) generateRandomID() (id string) { - var err error +func (fs *Filters) generateRandomID() (id string, err error) { buf := make([]byte, 20) for i := 0; i < 3; i++ { _, err = crand.Read(buf) @@ -70,16 +69,13 @@ func (fs *Filters) generateRandomID() (id string) { err = fmt.Errorf("error in generateRandomID: generated same ID twice") continue } - return id + return id, err } - // at this point all the attempts to generate the random ID failed. - // this poses a serious danger, since whisper very much relies - // on random numbers. the program must be terminated. - panic(err) + return "", err } -func (fs *Filters) Install(watcher *Filter) string { +func (fs *Filters) Install(watcher *Filter) (string, error) { if watcher.Messages == nil { watcher.Messages = make(map[common.Hash]*ReceivedMessage) } @@ -87,9 +83,11 @@ func (fs *Filters) Install(watcher *Filter) string { fs.mutex.Lock() defer fs.mutex.Unlock() - id := fs.generateRandomID() - fs.watchers[id] = watcher - return id + id, err := fs.generateRandomID() + if err == nil { + fs.watchers[id] = watcher + } + return id, err } func (fs *Filters) Uninstall(id string) { diff --git a/whisper/whisperv5/filter_test.go b/whisper/whisperv5/filter_test.go index 259b22a0d7..d69fb40dbf 100644 --- a/whisper/whisperv5/filter_test.go +++ b/whisper/whisperv5/filter_test.go @@ -100,9 +100,13 @@ func TestInstallFilters(t *testing.T) { filters := NewFilters(w) tst := generateTestCases(t, SizeTestFilters) + var err error var j string for i := 0; i < SizeTestFilters; i++ { - j = filters.Install(tst[i].f) + j, err = filters.Install(tst[i].f) + if err != nil { + t.Fatalf("seed %d: failed to install filter: %s", seed, err) + } tst[i].id = j if len(j) != 40 { t.Fatalf("seed %d: wrong filter id size [%d]", seed, len(j)) @@ -519,13 +523,17 @@ func TestWatchers(t *testing.T) { var j uint32 var e *Envelope var x, firstID string + var err error w := New() filters := NewFilters(w) tst := generateTestCases(t, NumFilters) for i = 0; i < NumFilters; i++ { tst[i].f.Src = nil - x = filters.Install(tst[i].f) + x, err = filters.Install(tst[i].f) + if err != nil { + t.Fatalf("failed to install filter with seed %d: %s.", seed, err) + } tst[i].id = x if len(firstID) == 0 { firstID = x diff --git a/whisper/whisperv5/peer_test.go b/whisper/whisperv5/peer_test.go index 223dd87620..cce2c92ba6 100644 --- a/whisper/whisperv5/peer_test.go +++ b/whisper/whisperv5/peer_test.go @@ -122,7 +122,10 @@ func initialize(t *testing.T) { topics := make([]TopicType, 0) topics = append(topics, sharedTopic) f := Filter{KeySym: sharedKey, Topics: topics} - node.filerId = node.shh.Watch(&f) + node.filerId, err = node.shh.Watch(&f) + if err != nil { + t.Fatalf("failed to install the filter: %s.", err) + } node.id, err = crypto.HexToECDSA(keys[i]) if err != nil { t.Fatalf("failed convert the key: %s.", keys[i]) diff --git a/whisper/whisperv5/whisper.go b/whisper/whisperv5/whisper.go index 87552b15ef..2a6ff5f409 100644 --- a/whisper/whisperv5/whisper.go +++ b/whisper/whisperv5/whisper.go @@ -272,7 +272,7 @@ func (w *Whisper) GetSymKey(name string) []byte { // Watch installs a new message handler to run in case a matching packet arrives // from the whisper network. -func (w *Whisper) Watch(f *Filter) string { +func (w *Whisper) Watch(f *Filter) (string, error) { return w.filters.Install(f) } From e026f53ab5e3746900367a0573fc458d27d1b126 Mon Sep 17 00:00:00 2001 From: Vlad Date: Wed, 22 Feb 2017 15:23:18 +0100 Subject: [PATCH 10/12] whisper: JSON field names fixed --- whisper/whisperv5/api.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/whisper/whisperv5/api.go b/whisper/whisperv5/api.go index 606d24c46f..4d33d23214 100644 --- a/whisper/whisperv5/api.go +++ b/whisper/whisperv5/api.go @@ -373,8 +373,8 @@ type PostArgs struct { Payload hexutil.Bytes `json:"payload"` WorkTime uint32 `json:"worktime"` PoW float64 `json:"pow"` - FilterID string `json:"filterid"` - PeerID hexutil.Bytes `json:"peerid"` + FilterID string `json:"filterID"` + PeerID hexutil.Bytes `json:"peerID"` } type WhisperFilterArgs struct { From 5e5b2b13007363da96d233a7e873fae5c83f513a Mon Sep 17 00:00:00 2001 From: Vlad Date: Thu, 23 Feb 2017 00:33:52 +0100 Subject: [PATCH 11/12] whisper: expiry refactoring --- whisper/whisperv5/api.go | 8 ++++ whisper/whisperv5/peer.go | 10 +---- whisper/whisperv5/whisper.go | 79 +++++++++++++++++++++++++----------- 3 files changed, 65 insertions(+), 32 deletions(-) diff --git a/whisper/whisperv5/api.go b/whisper/whisperv5/api.go index 4d33d23214..8be6d774f2 100644 --- a/whisper/whisperv5/api.go +++ b/whisper/whisperv5/api.go @@ -65,6 +65,14 @@ func (api *PublicWhisperAPI) Version() (hexutil.Uint, error) { return hexutil.Uint(api.whisper.Version()), nil } +// Stats returns the Whisper statistics for diagnostics. +func (api *PublicWhisperAPI) Stats() (string, error) { + if api.whisper == nil { + return "", whisperOffLineErr + } + return api.whisper.Stats(), nil +} + // MarkPeerTrusted marks specific peer trusted, which will allow it // to send historic (expired) messages. func (api *PublicWhisperAPI) MarkPeerTrusted(peerID hexutil.Bytes) error { diff --git a/whisper/whisperv5/peer.go b/whisper/whisperv5/peer.go index 42394a0a3f..0ae6998d6b 100644 --- a/whisper/whisperv5/peer.go +++ b/whisper/whisperv5/peer.go @@ -134,20 +134,14 @@ func (peer *Peer) marked(envelope *Envelope) bool { // expire iterates over all the known envelopes in the host and removes all // expired (unknown) ones from the known list. func (peer *Peer) expire() { - // Assemble the list of available envelopes - available := set.NewNonTS() - for _, envelope := range peer.host.Envelopes() { - available.Add(envelope.Hash()) - } - // Cross reference availability with known status unmark := make(map[common.Hash]struct{}) peer.known.Each(func(v interface{}) bool { - if !available.Has(v.(common.Hash)) { + if !peer.host.isEnvelopeCached(v.(common.Hash)) { unmark[v.(common.Hash)] = struct{}{} } return true }) - // Dump all known but unavailable + // Dump all known but no longer cached for hash := range unmark { peer.known.Remove(hash) } diff --git a/whisper/whisperv5/whisper.go b/whisper/whisperv5/whisper.go index 2a6ff5f409..f989b5fd59 100644 --- a/whisper/whisperv5/whisper.go +++ b/whisper/whisperv5/whisper.go @@ -36,6 +36,11 @@ import ( set "gopkg.in/fatih/set.v0" ) +type Statistics struct { + messages int + memory int +} + // Whisper represents a dark communication interface through the Ethereum // network, using its very own P2P communication layer. type Whisper struct { @@ -60,6 +65,8 @@ type Whisper struct { p2pMsgQueue chan *Envelope quit chan struct{} + stats Statistics + overflow bool test bool } @@ -288,7 +295,8 @@ func (w *Whisper) Unwatch(id string) { // Send injects a message into the whisper send queue, to be distributed in the // network in the coming cycles. func (w *Whisper) Send(envelope *Envelope) error { - return w.add(envelope) + _, err := w.add(envelope) + return err } // Start implements node.Service, starting the background data propagation thread @@ -361,11 +369,14 @@ func (wh *Whisper) runMessageLoop(p *Peer, rw p2p.MsgReadWriter) error { } // inject all envelopes into the internal pool for _, envelope := range envelopes { - if err := wh.add(envelope); err != nil { + cached, err := wh.add(envelope) + if err != nil { glog.V(logger.Warn).Infof("%v: bad envelope received: [%v], peer will be disconnected", p.peer, err) return fmt.Errorf("invalid envelope") } - p.mark(envelope) + if cached { + p.mark(envelope) + } } case p2pCode: // peer-to-peer message, sent directly to peer bypassing PoW checks, etc. @@ -402,13 +413,13 @@ func (wh *Whisper) runMessageLoop(p *Peer, rw p2p.MsgReadWriter) error { // add inserts a new envelope into the message pool to be distributed within the // whisper network. It also inserts the envelope into the expiration pool at the // appropriate time-stamp. In case of error, connection should be dropped. -func (wh *Whisper) add(envelope *Envelope) error { +func (wh *Whisper) add(envelope *Envelope) (bool, error) { now := uint32(time.Now().Unix()) sent := envelope.Expiry - envelope.TTL if sent > now { if sent-SynchAllowance > now { - return fmt.Errorf("envelope created in the future [%x]", envelope.Hash()) + return false, fmt.Errorf("envelope created in the future [%x]", envelope.Hash()) } else { // recalculate PoW, adjusted for the time difference, plus one second for latency envelope.calculatePoW(sent - now + 1) @@ -417,34 +428,34 @@ func (wh *Whisper) add(envelope *Envelope) error { if envelope.Expiry < now { if envelope.Expiry+SynchAllowance*2 < now { - return fmt.Errorf("very old message") + return false, fmt.Errorf("very old message") } else { glog.V(logger.Debug).Infof("expired envelope dropped [%x]", envelope.Hash()) - return nil // drop envelope without error + return false, nil // drop envelope without error } } if len(envelope.Data) > MaxMessageLength { - return fmt.Errorf("huge messages are not allowed [%x]", envelope.Hash()) + return false, fmt.Errorf("huge messages are not allowed [%x]", envelope.Hash()) } if len(envelope.Version) > 4 { - return fmt.Errorf("oversized version [%x]", envelope.Hash()) + return false, fmt.Errorf("oversized version [%x]", envelope.Hash()) } if len(envelope.AESNonce) > AESNonceMaxLength { // the standard AES GSM nonce size is 12, // but const gcmStandardNonceSize cannot be accessed directly - return fmt.Errorf("oversized AESNonce [%x]", envelope.Hash()) + return false, fmt.Errorf("oversized AESNonce [%x]", envelope.Hash()) } if len(envelope.Salt) > saltLength { - return fmt.Errorf("oversized salt [%x]", envelope.Hash()) + return false, fmt.Errorf("oversized salt [%x]", envelope.Hash()) } if envelope.PoW() < MinimumPoW && !wh.test { glog.V(logger.Debug).Infof("envelope with low PoW dropped: %f [%x]", envelope.PoW(), envelope.Hash()) - return nil // drop envelope without error + return false, nil // drop envelope without error } hash := envelope.Hash() @@ -471,7 +482,7 @@ func (wh *Whisper) add(envelope *Envelope) error { wh.mailServer.Archive(envelope) } } - return nil + return true, nil } // postEvent queues the message for further processing. @@ -546,22 +557,29 @@ func (w *Whisper) expire() { w.poolMu.Lock() defer w.poolMu.Unlock() + w.stats.clear() now := uint32(time.Now().Unix()) - for then, hashSet := range w.expirations { - // Short circuit if a future time - if then > now { - continue + for expiry, hashSet := range w.expirations { + if expiry < now { + w.stats.messages++ + + // Dump all expired messages and remove timestamp + hashSet.Each(func(v interface{}) bool { + w.stats.memory += w.envelopes[v.(common.Hash)].size() + delete(w.envelopes, v.(common.Hash)) + delete(w.messages, v.(common.Hash)) + return true + }) + w.expirations[expiry].Clear() + delete(w.expirations, expiry) } - // Dump all expired messages and remove timestamp - hashSet.Each(func(v interface{}) bool { - delete(w.envelopes, v.(common.Hash)) - delete(w.messages, v.(common.Hash)) - return true - }) - w.expirations[then].Clear() } } +func (w *Whisper) Stats() string { + return fmt.Sprintf("Latest expiry cycle cleared %d messages (%d bytes)", w.stats.messages, w.stats.memory) +} + // envelopes retrieves all the messages currently pooled by the node. func (w *Whisper) Envelopes() []*Envelope { w.poolMu.RLock() @@ -590,6 +608,14 @@ func (w *Whisper) Messages(id string) []*ReceivedMessage { return result } +func (w *Whisper) isEnvelopeCached(hash common.Hash) bool { + w.poolMu.Lock() + defer w.poolMu.Unlock() + + _, exist := w.envelopes[hash] + return exist +} + func (w *Whisper) addDecryptedMessage(msg *ReceivedMessage) { w.poolMu.Lock() defer w.poolMu.Unlock() @@ -597,6 +623,11 @@ func (w *Whisper) addDecryptedMessage(msg *ReceivedMessage) { w.messages[msg.EnvelopeHash] = msg } +func (s *Statistics) clear() { + s.memory = 0 + s.messages = 0 +} + func ValidatePublicKey(k *ecdsa.PublicKey) bool { return k != nil && k.X != nil && k.Y != nil && k.X.Sign() != 0 && k.Y.Sign() != 0 } From cb7a6d43c5477cd182333b1c01daa7bab548fb3f Mon Sep 17 00:00:00 2001 From: Vlad Date: Thu, 23 Feb 2017 00:51:54 +0100 Subject: [PATCH 12/12] whisper: statistics updated --- whisper/whisperv5/whisper.go | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/whisper/whisperv5/whisper.go b/whisper/whisperv5/whisper.go index f989b5fd59..771b7b3457 100644 --- a/whisper/whisperv5/whisper.go +++ b/whisper/whisperv5/whisper.go @@ -37,8 +37,9 @@ import ( ) type Statistics struct { - messages int - memory int + messagesCleared int + memoryCleared int + totalMemoryUsed int } // Whisper represents a dark communication interface through the Ethereum @@ -477,6 +478,7 @@ func (wh *Whisper) add(envelope *Envelope) (bool, error) { glog.V(logger.Detail).Infof("whisper envelope already cached [%x]\n", envelope.Hash()) } else { glog.V(logger.Detail).Infof("cached whisper envelope [%x]: %v\n", envelope.Hash(), envelope) + wh.stats.totalMemoryUsed += envelope.size() wh.postEvent(envelope, false) // notify the local node about the new message if wh.mailServer != nil { wh.mailServer.Archive(envelope) @@ -561,11 +563,13 @@ func (w *Whisper) expire() { now := uint32(time.Now().Unix()) for expiry, hashSet := range w.expirations { if expiry < now { - w.stats.messages++ + w.stats.messagesCleared++ // Dump all expired messages and remove timestamp hashSet.Each(func(v interface{}) bool { - w.stats.memory += w.envelopes[v.(common.Hash)].size() + sz := w.envelopes[v.(common.Hash)].size() + w.stats.memoryCleared += sz + w.stats.totalMemoryUsed -= sz delete(w.envelopes, v.(common.Hash)) delete(w.messages, v.(common.Hash)) return true @@ -577,7 +581,8 @@ func (w *Whisper) expire() { } func (w *Whisper) Stats() string { - return fmt.Sprintf("Latest expiry cycle cleared %d messages (%d bytes)", w.stats.messages, w.stats.memory) + return fmt.Sprintf("Latest expiry cycle cleared %d messages (%d bytes). Memory usage: %d bytes.", + w.stats.messagesCleared, w.stats.memoryCleared, w.stats.totalMemoryUsed) } // envelopes retrieves all the messages currently pooled by the node. @@ -624,8 +629,8 @@ func (w *Whisper) addDecryptedMessage(msg *ReceivedMessage) { } func (s *Statistics) clear() { - s.memory = 0 - s.messages = 0 + s.memoryCleared = 0 + s.messagesCleared = 0 } func ValidatePublicKey(k *ecdsa.PublicKey) bool {