diff --git a/cmd/wnode/main.go b/cmd/wnode/main.go index 9b634ecd43..0d824f9ef8 100644 --- a/cmd/wnode/main.go +++ b/cmd/wnode/main.go @@ -526,7 +526,7 @@ func messageLoop() { messages := f.Retrieve() for _, msg := range messages { if *fileExMode || len(msg.Payload) > 2048 { - saveMessageInFile(msg) + writeMessageToFile(*argSaveDir, msg) } else { printMessageInfo(msg) } @@ -553,7 +553,7 @@ func printMessageInfo(msg *whisper.ReceivedMessage) { } } -func saveMessageInFile(msg *whisper.ReceivedMessage) { +func writeMessageToFile(dir string, msg *whisper.ReceivedMessage) { timestamp := fmt.Sprintf("%d", msg.Sent) name := fmt.Sprintf("%x", msg.EnvelopeHash) @@ -565,8 +565,8 @@ func saveMessageInFile(msg *whisper.ReceivedMessage) { if whisper.IsPubKeyEqual(msg.Src, &asymKey.PublicKey) { // message from myself: don't save, only report fmt.Printf("\n%s <%x>: message received: '%s'\n", timestamp, address, name) - } else if len(*argSaveDir) > 0 { - fullpath := filepath.Join(*argSaveDir, name) + } else if len(dir) > 0 { + fullpath := filepath.Join(dir, name) err := ioutil.WriteFile(fullpath, msg.Payload, 0644) if err != nil { fmt.Printf("\n%s {%x}: message received but not saved: %s\n", timestamp, address, err) diff --git a/whisper/whisperv5/api.go b/whisper/whisperv5/api.go index 4d96507a8e..a93ac9baf1 100644 --- a/whisper/whisperv5/api.go +++ b/whisper/whisperv5/api.go @@ -24,11 +24,10 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/p2p/discover" ) -var whisperOffLineErr = errors.New("whisper is offline") +var whisperOfflineErr = errors.New("whisper is offline") // PublicWhisperAPI provides the whisper RPC service. type PublicWhisperAPI struct { @@ -43,7 +42,7 @@ func NewPublicWhisperAPI(w *Whisper) *PublicWhisperAPI { // Start starts the Whisper worker threads. func (api *PublicWhisperAPI) Start() error { if api.whisper == nil { - return whisperOffLineErr + return whisperOfflineErr } return api.whisper.Start(nil) } @@ -51,7 +50,7 @@ func (api *PublicWhisperAPI) Start() error { // Stop stops the Whisper worker threads. func (api *PublicWhisperAPI) Stop() error { if api.whisper == nil { - return whisperOffLineErr + return whisperOfflineErr } return api.whisper.Stop() } @@ -59,29 +58,31 @@ func (api *PublicWhisperAPI) Stop() error { // Version returns the Whisper version this node offers. func (api *PublicWhisperAPI) Version() (hexutil.Uint, error) { if api.whisper == nil { - return 0, whisperOffLineErr + return 0, whisperOfflineErr } return hexutil.Uint(api.whisper.Version()), nil } -// Stats returns the Whisper statistics for diagnostics. +// Info returns the Whisper statistics for diagnostics. func (api *PublicWhisperAPI) Info() (string, error) { if api.whisper == nil { - return "", whisperOffLineErr + return "", whisperOfflineErr } return api.whisper.Stats(), nil } +// SetMaxMessageLength sets the maximal message length allowed by this node func (api *PublicWhisperAPI) SetMaxMessageLength(val int) error { if api.whisper == nil { - return whisperOffLineErr + return whisperOfflineErr } return api.whisper.SetMaxMessageLength(val) } +// SetMinimumPoW sets the minimal PoW required by this node func (api *PublicWhisperAPI) SetMinimumPoW(val float64) error { if api.whisper == nil { - return whisperOffLineErr + return whisperOfflineErr } return api.whisper.SetMinimumPoW(val) } @@ -90,71 +91,69 @@ func (api *PublicWhisperAPI) SetMinimumPoW(val float64) error { // to send historic (expired) messages. func (api *PublicWhisperAPI) AllowP2PMessagesFromPeer(enode string) error { if api.whisper == nil { - return whisperOffLineErr + return whisperOfflineErr } n, err := discover.ParseNode(enode) if err != nil { - info := "Failed to parse enode of trusted peer: " + err.Error() - log.Error(info) - return errors.New(info) + return errors.New("failed to parse enode of trusted peer: " + err.Error()) } return api.whisper.AllowP2PMessagesFromPeer(n.ID[:]) } -// RequestHistoricMessages requests the peer to deliver the old (expired) messages. -// data contains parameters (time frame, payment details, etc.), required -// by the remote email-like server. Whisper is not aware about the data format, -// it will just forward the raw data to the server. -//func (api *PublicWhisperAPI) RequestHistoricMessages(peerID hexutil.Bytes, data hexutil.Bytes) error { -// if api.whisper == nil { -// return whisperOffLineErr -// } -// return api.whisper.RequestHistoricMessages(peerID, data) -//} - -// HasIdentity checks if the whisper node is configured with the private key +// HasKeyPair checks if the whisper node is configured with the private key // of the specified public pair. func (api *PublicWhisperAPI) HasKeyPair(id string) (bool, error) { if api.whisper == nil { - return false, whisperOffLineErr + return false, whisperOfflineErr } return api.whisper.HasKeyPair(id), nil } -// DeleteIdentity deletes the specifies key if it exists. +// DeleteKeyPair deletes the specifies key if it exists. func (api *PublicWhisperAPI) DeleteKeyPair(id string) (bool, error) { if api.whisper == nil { - return false, whisperOffLineErr + return false, whisperOfflineErr } - success := api.whisper.DeleteKeyPair(id) - return success, nil + return api.whisper.DeleteKeyPair(id), nil } // NewKeyPair generates a new cryptographic identity for the client, and injects // it into the known identities for message decryption. func (api *PublicWhisperAPI) NewKeyPair() (string, error) { if api.whisper == nil { - return "", whisperOffLineErr + return "", whisperOfflineErr } return api.whisper.NewKeyPair() } // GetPublicKey returns the public key for identity id -func (api *PublicWhisperAPI) GetPublicKey(id string) (string, error) { +func (api *PublicWhisperAPI) GetPublicKey(id string) (hexutil.Bytes, error) { if api.whisper == nil { - return "", whisperOffLineErr + return nil, whisperOfflineErr } key, err := api.whisper.GetPrivateKey(id) if err != nil { - return "", err + return nil, err } - return common.ToHex(crypto.FromECDSAPub(&key.PublicKey)), nil + return crypto.FromECDSAPub(&key.PublicKey), nil } +// todo: delete is tests pass +//func (api *PublicWhisperAPI) GetPublicKey(id string) (string, error) { +// if api.whisper == nil { +// return "", whisperOffLineErr +// } +// key, err := api.whisper.GetPrivateKey(id) +// if err != nil { +// return "", err +// } +// return common.ToHex(crypto.FromECDSAPub(&key.PublicKey)), nil +//} + // GetPrivateKey returns the private key for identity id func (api *PublicWhisperAPI) GetPrivateKey(id string) (string, error) { if api.whisper == nil { - return "", whisperOffLineErr + return "", whisperOfflineErr } key, err := api.whisper.GetPrivateKey(id) if err != nil { @@ -163,57 +162,71 @@ func (api *PublicWhisperAPI) GetPrivateKey(id string) (string, error) { return common.ToHex(crypto.FromECDSA(key)), nil } -// GenerateSymKey generates a random symmetric key and stores it under id, +// GenerateSymmetricKey generates a random symmetric key and stores it under id, // which is then returned. Will be used in the future for session key exchange. func (api *PublicWhisperAPI) GenerateSymmetricKey() (string, error) { if api.whisper == nil { - return "", whisperOffLineErr + return "", whisperOfflineErr } return api.whisper.GenerateSymKey() } -// AddSymKeyDirect stores the key, and returns its id. +// AddSymmetricKeyDirect stores the key, and returns its id. func (api *PublicWhisperAPI) AddSymmetricKeyDirect(key hexutil.Bytes) (string, error) { if api.whisper == nil { - return "", whisperOffLineErr + return "", whisperOfflineErr } return api.whisper.AddSymKeyDirect(key) } -// AddSymKeyFromPassword generates the key from password, stores it, and returns its id. +// AddSymmetricKeyFromPassword generates the key from password, stores it, and returns its id. func (api *PublicWhisperAPI) AddSymmetricKeyFromPassword(password string) (string, error) { if api.whisper == nil { - return "", whisperOffLineErr + return "", whisperOfflineErr } return api.whisper.AddSymKeyFromPassword(password) } -// HasSymKey returns true if there is a key associated with the given id. +// HasSymmetricKey returns true if there is a key associated with the given id. // Otherwise returns false. func (api *PublicWhisperAPI) HasSymmetricKey(id string) (bool, error) { if api.whisper == nil { - return false, whisperOffLineErr + return false, whisperOfflineErr } res := api.whisper.HasSymKey(id) return res, nil } -func (api *PublicWhisperAPI) GetSymmetricKey(name string) (string, error) { +// GetSymmetricKey returns the symmetric key associated with the given id. +func (api *PublicWhisperAPI) GetSymmetricKey(name string) (hexutil.Bytes, error) { if api.whisper == nil { - return "", whisperOffLineErr + return nil, whisperOfflineErr } - b, err := api.whisper.GetSymKey(name) if err != nil { - return "", err + return nil, err } - return fmt.Sprintf("%x", b), nil + return b, nil } -// DeleteSymKey deletes the key associated with the name string if it exists. +// todo: delete if tests pass +//func (api *PublicWhisperAPI) GetSymmetricKey(name string) (string, error) { +// if api.whisper == nil { +// return "", whisperOffLineErr +// } +// +// b, err := api.whisper.GetSymKey(name) +// if err != nil { +// return "", err +// } +// //return fmt.Sprintf("%x", b), nil // todo: delete if tests pass +// return common.ToHex(b), nil +//} + +// DeleteSymmetricKey deletes the key associated with the name string if it exists. func (api *PublicWhisperAPI) DeleteSymmetricKey(name string) (bool, error) { if api.whisper == nil { - return false, whisperOffLineErr + return false, whisperOfflineErr } res := api.whisper.DeleteSymKey(name) return res, nil @@ -223,7 +236,7 @@ func (api *PublicWhisperAPI) DeleteSymmetricKey(name string) (bool, error) { // Returns the ID of the newly created filter. func (api *PublicWhisperAPI) Subscribe(args WhisperFilterArgs) (string, error) { if api.whisper == nil { - return "", whisperOffLineErr + return "", whisperOfflineErr } filter := Filter{ @@ -233,61 +246,44 @@ func (api *PublicWhisperAPI) Subscribe(args WhisperFilterArgs) (string, error) { AllowP2P: args.AllowP2P, } + var err error for i, bt := range args.Topics { if len(bt) == 0 || len(bt) > 4 { - info := fmt.Sprintf("Subscribe: topic %d has wrong size: %d", i, len(bt)) - log.Error(info) - return "", errors.New(info) + return "", errors.New(fmt.Sprintf("subscribe: topic %d has wrong size: %d", i, len(bt))) } filter.Topics = append(filter.Topics, bt) } - err := ValidateKeyID(args.Key) - if err != nil { - info := "Subscribe: " + err.Error() - log.Error(info) - return "", errors.New(info) + if err = ValidateKeyID(args.Key); err != nil { + return "", errors.New("subscribe: " + err.Error()) } if len(args.SignedWith) > 0 { if !ValidatePublicKey(filter.Src) { - info := "Subscribe: Invalid 'SignedWith' field" - log.Error(info) - return "", errors.New(info) + return "", errors.New("subscribe: invalid 'SignedWith' field") } } if args.Symmetric { if len(args.Topics) == 0 { - info := "Subscribe: at least one topic must be specified with symmetric encryption" - log.Error(info) - return "", errors.New(info) + return "", errors.New("subscribe: at least one topic must be specified with symmetric encryption") } symKey, err := api.whisper.GetSymKey(args.Key) if err != nil { - info := "Subscribe: invalid key ID" - log.Error(info) - return "", errors.New(info) + return "", errors.New("subscribe: invalid key ID") } if !validateSymmetricKey(symKey) { - info := "Subscribe: retrieved key is invalid" - log.Error(info) - return "", errors.New(info) + return "", errors.New("subscribe: retrieved key is invalid") } - filter.KeySym = symKey filter.SymKeyHash = crypto.Keccak256Hash(filter.KeySym) } else { filter.KeyAsym, err = api.whisper.GetPrivateKey(args.Key) if err != nil { - info := "Subscribe: invalid key ID" - log.Error(info) - return "", errors.New(info) + return "", errors.New("subscribe: invalid key ID") } if filter.KeyAsym == nil { - info := "Subscribe: non-existent identity provided" - log.Error(info) - return "", errors.New(info) + return "", errors.New("subscribe: non-existent identity provided") } } @@ -299,7 +295,7 @@ func (api *PublicWhisperAPI) Unsubscribe(id string) { api.whisper.Unsubscribe(id) } -// GetFilterChanges retrieves all the new messages matched by a filter since the last retrieval. +// GetSubscriptionMessages retrieves all the new messages matched by a filter since the last retrieval. func (api *PublicWhisperAPI) GetSubscriptionMessages(filterId string) []*WhisperMessage { f := api.whisper.GetFilter(filterId) if f != nil { @@ -309,7 +305,8 @@ func (api *PublicWhisperAPI) GetSubscriptionMessages(filterId string) []*Whisper return toWhisperMessages(nil) } -// GetMessages retrieves all the known messages that match a specific filter. +// GetMessages retrieves all the floating messages that match a specific filter. +// It is likely to be called once per session, right after Subscribe call. func (api *PublicWhisperAPI) GetMessages(filterId string) []*WhisperMessage { all := api.whisper.Messages(filterId) return toWhisperMessages(all) @@ -327,7 +324,7 @@ func toWhisperMessages(messages []*ReceivedMessage) []*WhisperMessage { // Post creates a whisper message and injects it into the network for distribution. func (api *PublicWhisperAPI) Post(args PostArgs) error { if api.whisper == nil { - return whisperOffLineErr + return whisperOfflineErr } var err error @@ -340,96 +337,69 @@ func (api *PublicWhisperAPI) Post(args PostArgs) error { } if len(args.Key) == 0 { - info := "Post: key is missing" - log.Error(info) - return errors.New(info) + return errors.New("post: key is missing") } if len(args.SignWith) > 0 { params.Src, err = api.whisper.GetPrivateKey(args.SignWith) if err != nil { - log.Error(err.Error()) return err } if params.Src == nil { - info := "Post: empty identity" - log.Error(info) - return errors.New(info) + return errors.New("post: empty identity") } } if len(args.Topic) == TopicLength { params.Topic = BytesToTopic(args.Topic) } else if len(args.Topic) != 0 { - info := fmt.Sprintf("Post: wrong topic size %d", len(args.Topic)) - log.Error(info) - return errors.New(info) + return errors.New(fmt.Sprintf("post: wrong topic size %d", len(args.Topic))) } if args.Type == "sym" { - err = ValidateKeyID(args.Key) - if err != nil { - log.Error(err.Error()) + if err = ValidateKeyID(args.Key); err != nil { return err } params.KeySym, err = api.whisper.GetSymKey(args.Key) if err != nil { - log.Error(err.Error()) return err } if !validateSymmetricKey(params.KeySym) { - info := "Post: key for symmetric encryption is invalid" - log.Error(info) - return errors.New(info) + return errors.New("post: key for symmetric encryption is invalid") } if len(params.Topic) == 0 { - info := "Post: topic is missing for symmetric encryption" - log.Error(info) - return errors.New(info) + return errors.New("post: topic is missing for symmetric encryption") } } else if args.Type == "asym" { params.Dst = crypto.ToECDSAPub(common.FromHex(args.Key)) if !ValidatePublicKey(params.Dst) { - info := "Post: public key for asymmetric encryption is invalid" - log.Error(info) - return errors.New(info) + return errors.New("post: public key for asymmetric encryption is invalid") } } else { - info := "Post: wrong type (sym/asym)" - log.Error(info) - return errors.New(info) + return errors.New("post: wrong type (sym/asym)") } // encrypt and send message := NewSentMessage(¶ms) if message == nil { - info := "Post: failed create new message, probably due to failed rand function (OS level)" - log.Error(info) - return errors.New(info) + return errors.New("post: failed create new message, probably due to failed rand function (OS level)") } envelope, err := message.Wrap(¶ms) if err != nil { - log.Error(err.Error()) return err } if envelope.size() > api.whisper.maxMsgLength { - info := "Post: message is too big" - log.Error(info) - return errors.New(info) + return errors.New("post: message is too big") } if len(args.TargetPeer) != 0 { n, err := discover.ParseNode(args.TargetPeer) if err != nil { - info := "Post: failed to parse enode of target peer: " + err.Error() - log.Error(info) - return errors.New(info) + return errors.New("post: failed to parse enode of target peer: " + err.Error()) } return api.whisper.SendP2PMessage(n.ID[:], envelope) } else if args.PowTarget < api.whisper.minPoW { - info := "Post: target PoW is less than minimum PoW, the message can not be sent" - log.Error(info) - return errors.New(info) + return errors.New("post: target PoW is less than minimum PoW, the message can not be sent") } return api.whisper.Send(envelope) @@ -473,12 +443,13 @@ func (args *WhisperFilterArgs) UnmarshalJSON(b []byte) (err error) { return err } - if obj.Type == "sym" { + switch obj.Type { + case "sym": args.Symmetric = true - } else if obj.Type == "asym" { + case "asym": args.Symmetric = false - } else { - return fmt.Errorf("Wrong type (sym/asym") + default: + return errors.New("wrong type (sym/asym") } args.Key = obj.Key diff --git a/whisper/whisperv5/filter.go b/whisper/whisperv5/filter.go index 8230a26ff0..3f37642bd4 100644 --- a/whisper/whisperv5/filter.go +++ b/whisper/whisperv5/filter.go @@ -94,11 +94,11 @@ func (fs *Filters) NotifyWatchers(env *Envelope, p2pMessage bool) { fs.mutex.RLock() defer fs.mutex.RUnlock() - j := -1 + i := -1 // only used for logging info for _, watcher := range fs.watchers { - j++ + i++ if p2pMessage && !watcher.AllowP2P { - log.Trace(fmt.Sprintf("msg [%x], filter [%d]: p2p messages are not allowed", env.Hash(), j)) + log.Trace(fmt.Sprintf("msg [%x], filter [%d]: p2p messages are not allowed", env.Hash(), i)) continue } @@ -110,10 +110,10 @@ func (fs *Filters) NotifyWatchers(env *Envelope, p2pMessage bool) { if match { msg = env.Open(watcher) if msg == nil { - log.Trace(fmt.Sprintf("msg [%x], filter [%d]: failed to open", env.Hash(), j)) + log.Trace(fmt.Sprintf("msg [%x], filter [%d]: failed to open", env.Hash(), i)) } } else { - log.Trace(fmt.Sprintf("msg [%x], filter [%d]: does not match", env.Hash(), j)) + log.Trace(fmt.Sprintf("msg [%x], filter [%d]: does not match", env.Hash(), i)) } } @@ -203,16 +203,16 @@ func (f *Filter) MatchTopic(topic TopicType) bool { } for _, bt := range f.Topics { - if MatchSingleTopic(topic, bt) { + if matchSingleTopic(topic, bt) { return true } } return false } -func MatchSingleTopic(topic TopicType, bt []byte) bool { +func matchSingleTopic(topic TopicType, bt []byte) bool { if len(bt) > 4 { - bt = bt[0:4] + bt = bt[:4] } for j, b := range bt { diff --git a/whisper/whisperv5/message.go b/whisper/whisperv5/message.go index fa7d59d81e..7b0a3be714 100644 --- a/whisper/whisperv5/message.go +++ b/whisper/whisperv5/message.go @@ -26,7 +26,6 @@ import ( "crypto/sha256" "errors" "fmt" - mrand "math/rand" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" @@ -132,7 +131,7 @@ func (msg *SentMessage) appendPadding(params *MessageParams) error { panic("please fix the padding algorithm before releasing new version") } buf := make([]byte, padSize) - _, err := mrand.Read(buf[1:]) + _, err := crand.Read(buf[1:]) if err != nil { return err } diff --git a/whisper/whisperv5/peer_test.go b/whisper/whisperv5/peer_test.go index 47c9942cb6..a1a9a1ffa2 100644 --- a/whisper/whisperv5/peer_test.go +++ b/whisper/whisperv5/peer_test.go @@ -119,8 +119,10 @@ func initialize(t *testing.T) { topics := make([]TopicType, 0) topics = append(topics, sharedTopic) f := Filter{KeySym: sharedKey} - f.Topics = make([][]byte, 1) - f.Topics[0] = topics[0][:] + f.Topics = [][]byte{topics[0][:]} + // todo: delete if pass + //f.Topics = make([][]byte, 1) + //f.Topics[0] = topics[0][:] node.filerId, err = node.shh.Watch(&f) if err != nil { t.Fatalf("failed to install the filter: %s.", err) diff --git a/whisper/whisperv5/whisper.go b/whisper/whisperv5/whisper.go index c5f06f16eb..a0f8bd97ba 100644 --- a/whisper/whisperv5/whisper.go +++ b/whisper/whisperv5/whisper.go @@ -46,12 +46,12 @@ type Statistics struct { // Whisper represents a dark communication interface through the Ethereum // network, using its very own P2P communication layer. type Whisper struct { - protocol p2p.Protocol - filters *Filters + protocol p2p.Protocol // Protocol description and parameters + filters *Filters // Message filters installed with Subscribe function - privateKeys map[string]*ecdsa.PrivateKey - symKeys map[string][]byte - keyMu sync.RWMutex + privateKeys map[string]*ecdsa.PrivateKey // Private key storage + symKeys map[string][]byte // Symmetric key storage + keyMu sync.RWMutex // Mutex associated with key storages envelopes map[common.Hash]*Envelope // Pool of envelopes currently tracked by this node expirations map[uint32]*set.SetNonTS // Message expiration pool @@ -60,17 +60,17 @@ type Whisper struct { peers map[*Peer]struct{} // Set of currently active peers peerMu sync.RWMutex // Mutex to sync the active peer set - mailServer MailServer + messageQueue chan *Envelope // Message queue for normal whisper messages + p2pMsgQueue chan *Envelope // Message queue for peer-to-peer messages (not to be forwarded any further) + quit chan struct{} // Channel used for graceful exit - messageQueue chan *Envelope - p2pMsgQueue chan *Envelope - quit chan struct{} + minPoW float64 // Minimal PoW required by the whisper node + maxMsgLength int // Maximal message length allowed by the whisper node + overflow bool // Indicator of message queue overflow - stats Statistics + stats Statistics // Statistics of whisper node - minPoW float64 - maxMsgLength int - overflow bool + mailServer MailServer // MailServer interface } // New creates a Whisper client ready to communicate through the Ethereum P2P network. @@ -113,6 +113,8 @@ func (w *Whisper) APIs() []rpc.API { } } +// RegisterServer registers MailServer interface. +// MailServer will process all the incoming messages with p2pRequestCode. func (w *Whisper) RegisterServer(server MailServer) { w.mailServer = server } @@ -127,6 +129,7 @@ func (w *Whisper) Version() uint { return w.protocol.Version } +// SetMaxMessageLength sets the maximal message length allowed by this node func (w *Whisper) SetMaxMessageLength(val int) error { if val <= 0 { return fmt.Errorf("Invalid message length: %d", val) @@ -135,6 +138,7 @@ func (w *Whisper) SetMaxMessageLength(val int) error { return nil } +// SetMinimumPoW sets the minimal PoW required by this node func (w *Whisper) SetMinimumPoW(val float64) error { if val <= 0.0 { return fmt.Errorf("Invalid PoW: %f", val) @@ -166,6 +170,11 @@ func (w *Whisper) AllowP2PMessagesFromPeer(peerID []byte) error { return nil } +// RequestHistoricMessages sends a message with p2pRequestCode to a specific peer, +// which is known to implement MailServer interface, and is supposed to process this +// request and respond with a number of peer-to-peer messages (possibly expired), +// which are not supposed to be forwarded any further. +// The whisper protocol is agnostic of the format and contents of envelope. func (w *Whisper) RequestHistoricMessages(peerID []byte, envelope *Envelope) error { p, err := w.getPeer(peerID) if err != nil { @@ -175,14 +184,17 @@ func (w *Whisper) RequestHistoricMessages(peerID []byte, envelope *Envelope) err return p2p.Send(p.ws, p2pRequestCode, envelope) } +// SendP2PMessage sends a peer-to-peer message to a specific peer. func (w *Whisper) SendP2PMessage(peerID []byte, envelope *Envelope) error { p, err := w.getPeer(peerID) if err != nil { return err } - return p2p.Send(p.ws, p2pCode, envelope) + //return p2p.Send(p.ws, p2pCode, envelope) // todo: delete if tests pass + return w.SendP2PDirect(p, envelope) } +// SendP2PDirect sends a peer-to-peer message to a specific peer. func (w *Whisper) SendP2PDirect(peer *Peer, envelope *Envelope) error { return p2p.Send(peer.ws, p2pCode, envelope) } @@ -247,6 +259,8 @@ func (w *Whisper) GetPrivateKey(id string) (*ecdsa.PrivateKey, error) { return key, nil } +// GenerateSymKey generates a random symmetric key and stores it under id, +// which is then returned. Will be used in the future for session key exchange. func (w *Whisper) GenerateSymKey() (string, error) { const size = aesKeyLength * 2 buf := make([]byte, size) @@ -281,6 +295,7 @@ func (w *Whisper) GenerateSymKey() (string, error) { return id, nil } +// AddSymKeyDirect stores the key, and returns its id. func (w *Whisper) AddSymKeyDirect(key []byte) (string, error) { if len(key) != aesKeyLength { return "", fmt.Errorf("Wrong key size: %d", len(key)) @@ -301,6 +316,7 @@ func (w *Whisper) AddSymKeyDirect(key []byte) (string, error) { return id, nil } +// AddSymKeyFromPassword generates the key from password, stores it, and returns its id. func (w *Whisper) AddSymKeyFromPassword(password string) (string, error) { id, err := GenerateRandomID() if err != nil { @@ -326,12 +342,15 @@ func (w *Whisper) AddSymKeyFromPassword(password string) (string, error) { return id, nil } +// HasSymKey returns true if there is a key associated with the given id. +// Otherwise returns false. func (w *Whisper) HasSymKey(id string) bool { w.keyMu.RLock() defer w.keyMu.RUnlock() return w.symKeys[id] != nil } +// DeleteSymKey deletes the key associated with the name string if it exists. func (w *Whisper) DeleteSymKey(id string) bool { w.keyMu.Lock() defer w.keyMu.Unlock() @@ -342,6 +361,7 @@ func (w *Whisper) DeleteSymKey(id string) bool { return false } +// GetSymKey returns the symmetric key associated with the given id. func (w *Whisper) GetSymKey(id string) ([]byte, error) { w.keyMu.RLock() defer w.keyMu.RUnlock() @@ -351,12 +371,13 @@ func (w *Whisper) GetSymKey(id string) ([]byte, error) { return nil, fmt.Errorf("non-existent key ID") } -// Watch installs a new message handler to run in case a matching packet arrives -// from the whisper network. +// Watch installs a new message handler used for filtering, decrypting +// and subsequent storing of incoming messages. func (w *Whisper) Watch(f *Filter) (string, error) { return w.filters.Install(f) } +// GetFilter returns the filter by id. func (w *Whisper) GetFilter(id string) *Filter { return w.filters.Get(id) } @@ -662,6 +683,7 @@ func (w *Whisper) expire() { } } +// Stats returns the whisper node statistics. func (w *Whisper) Stats() string { result := fmt.Sprintf("Memory usage: %d bytes. Average messages cleared per expiry cycle: %d. Total messages cleared: %d.", w.stats.memoryUsed, w.stats.totalMessagesCleared/w.stats.cycles, w.stats.totalMessagesCleared) @@ -669,6 +691,9 @@ func (w *Whisper) Stats() string { result += fmt.Sprintf(" Latest expiry cycle cleared %d messages (%d bytes).", w.stats.messagesCleared, w.stats.memoryCleared) } + if w.overflow { + result += " Message queue state: overflow." + } return result } @@ -702,6 +727,7 @@ func (w *Whisper) Messages(id string) []*ReceivedMessage { return result } +// isEnvelopeCached checks if envelope with specific hash has already been received and cached. func (w *Whisper) isEnvelopeCached(hash common.Hash) bool { w.poolMu.Lock() defer w.poolMu.Unlock() @@ -710,6 +736,7 @@ func (w *Whisper) isEnvelopeCached(hash common.Hash) bool { return exist } +// reset resets the node's statistics after each expiry cycle. func (s *Statistics) reset() { s.cycles++ s.totalMessagesCleared += s.messagesCleared @@ -718,6 +745,7 @@ func (s *Statistics) reset() { s.messagesCleared = 0 } +// ValidateKeyID checks the format of key id. func ValidateKeyID(id string) error { const target = keyIdSize * 2 if len(id) != target { @@ -726,10 +754,12 @@ func ValidateKeyID(id string) error { return nil } +// ValidatePublicKey checks the format of the given public key. func ValidatePublicKey(k *ecdsa.PublicKey) bool { return k != nil && k.X != nil && k.Y != nil && k.X.Sign() != 0 && k.Y.Sign() != 0 } +// validatePrivateKey checks the format of the given private key. func validatePrivateKey(k *ecdsa.PrivateKey) bool { if k == nil || k.D == nil || k.D.Sign() == 0 { return false @@ -742,6 +772,7 @@ func validateSymmetricKey(k []byte) bool { return len(k) > 0 && !containsOnlyZeros(k) } +// containsOnlyZeros checks if the data contain only zeros. func containsOnlyZeros(data []byte) bool { for _, b := range data { if b != 0 { @@ -751,6 +782,7 @@ func containsOnlyZeros(data []byte) bool { return true } +// bytesToIntLittleEndian converts the slice to 64-bit unsigned integer. func bytesToIntLittleEndian(b []byte) (res uint64) { mul := uint64(1) for i := 0; i < len(b); i++ { @@ -760,6 +792,7 @@ func bytesToIntLittleEndian(b []byte) (res uint64) { return res } +// BytesToIntBigEndian converts the slice to 64-bit unsigned integer. func BytesToIntBigEndian(b []byte) (res uint64) { for i := 0; i < len(b); i++ { res *= 256 @@ -781,6 +814,7 @@ func deriveKeyMaterial(key []byte, version uint64) (derivedKey []byte, err error } } +// GenerateRandomID generates a random string, which is then returned to be used as a key id func GenerateRandomID() (id string, err error) { buf := make([]byte, keyIdSize) _, err = crand.Read(buf)