whisper: refactoring (documenting mostly)

This commit is contained in:
Vlad 2017-03-24 19:14:11 +01:00
parent 3b57070408
commit b6b8488390
6 changed files with 163 additions and 157 deletions

View file

@ -526,7 +526,7 @@ func messageLoop() {
messages := f.Retrieve() messages := f.Retrieve()
for _, msg := range messages { for _, msg := range messages {
if *fileExMode || len(msg.Payload) > 2048 { if *fileExMode || len(msg.Payload) > 2048 {
saveMessageInFile(msg) writeMessageToFile(*argSaveDir, msg)
} else { } else {
printMessageInfo(msg) 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) timestamp := fmt.Sprintf("%d", msg.Sent)
name := fmt.Sprintf("%x", msg.EnvelopeHash) name := fmt.Sprintf("%x", msg.EnvelopeHash)
@ -565,8 +565,8 @@ func saveMessageInFile(msg *whisper.ReceivedMessage) {
if whisper.IsPubKeyEqual(msg.Src, &asymKey.PublicKey) { if whisper.IsPubKeyEqual(msg.Src, &asymKey.PublicKey) {
// message from myself: don't save, only report // message from myself: don't save, only report
fmt.Printf("\n%s <%x>: message received: '%s'\n", timestamp, address, name) fmt.Printf("\n%s <%x>: message received: '%s'\n", timestamp, address, name)
} else if len(*argSaveDir) > 0 { } else if len(dir) > 0 {
fullpath := filepath.Join(*argSaveDir, name) fullpath := filepath.Join(dir, name)
err := ioutil.WriteFile(fullpath, msg.Payload, 0644) err := ioutil.WriteFile(fullpath, msg.Payload, 0644)
if err != nil { if err != nil {
fmt.Printf("\n%s {%x}: message received but not saved: %s\n", timestamp, address, err) fmt.Printf("\n%s {%x}: message received but not saved: %s\n", timestamp, address, err)

View file

@ -24,11 +24,10 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/p2p/discover" "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. // PublicWhisperAPI provides the whisper RPC service.
type PublicWhisperAPI struct { type PublicWhisperAPI struct {
@ -43,7 +42,7 @@ func NewPublicWhisperAPI(w *Whisper) *PublicWhisperAPI {
// Start starts the Whisper worker threads. // Start starts the Whisper worker threads.
func (api *PublicWhisperAPI) Start() error { func (api *PublicWhisperAPI) Start() error {
if api.whisper == nil { if api.whisper == nil {
return whisperOffLineErr return whisperOfflineErr
} }
return api.whisper.Start(nil) return api.whisper.Start(nil)
} }
@ -51,7 +50,7 @@ func (api *PublicWhisperAPI) Start() error {
// Stop stops the Whisper worker threads. // Stop stops the Whisper worker threads.
func (api *PublicWhisperAPI) Stop() error { func (api *PublicWhisperAPI) Stop() error {
if api.whisper == nil { if api.whisper == nil {
return whisperOffLineErr return whisperOfflineErr
} }
return api.whisper.Stop() return api.whisper.Stop()
} }
@ -59,29 +58,31 @@ func (api *PublicWhisperAPI) Stop() error {
// Version returns the Whisper version this node offers. // Version returns the Whisper version this node offers.
func (api *PublicWhisperAPI) Version() (hexutil.Uint, error) { func (api *PublicWhisperAPI) Version() (hexutil.Uint, error) {
if api.whisper == nil { if api.whisper == nil {
return 0, whisperOffLineErr return 0, whisperOfflineErr
} }
return hexutil.Uint(api.whisper.Version()), nil 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) { func (api *PublicWhisperAPI) Info() (string, error) {
if api.whisper == nil { if api.whisper == nil {
return "", whisperOffLineErr return "", whisperOfflineErr
} }
return api.whisper.Stats(), nil return api.whisper.Stats(), nil
} }
// SetMaxMessageLength sets the maximal message length allowed by this node
func (api *PublicWhisperAPI) SetMaxMessageLength(val int) error { func (api *PublicWhisperAPI) SetMaxMessageLength(val int) error {
if api.whisper == nil { if api.whisper == nil {
return whisperOffLineErr return whisperOfflineErr
} }
return api.whisper.SetMaxMessageLength(val) return api.whisper.SetMaxMessageLength(val)
} }
// SetMinimumPoW sets the minimal PoW required by this node
func (api *PublicWhisperAPI) SetMinimumPoW(val float64) error { func (api *PublicWhisperAPI) SetMinimumPoW(val float64) error {
if api.whisper == nil { if api.whisper == nil {
return whisperOffLineErr return whisperOfflineErr
} }
return api.whisper.SetMinimumPoW(val) return api.whisper.SetMinimumPoW(val)
} }
@ -90,71 +91,69 @@ func (api *PublicWhisperAPI) SetMinimumPoW(val float64) error {
// to send historic (expired) messages. // to send historic (expired) messages.
func (api *PublicWhisperAPI) AllowP2PMessagesFromPeer(enode string) error { func (api *PublicWhisperAPI) AllowP2PMessagesFromPeer(enode string) error {
if api.whisper == nil { if api.whisper == nil {
return whisperOffLineErr return whisperOfflineErr
} }
n, err := discover.ParseNode(enode) n, err := discover.ParseNode(enode)
if err != nil { if err != nil {
info := "Failed to parse enode of trusted peer: " + err.Error() return errors.New("failed to parse enode of trusted peer: " + err.Error())
log.Error(info)
return errors.New(info)
} }
return api.whisper.AllowP2PMessagesFromPeer(n.ID[:]) return api.whisper.AllowP2PMessagesFromPeer(n.ID[:])
} }
// RequestHistoricMessages requests the peer to deliver the old (expired) messages. // HasKeyPair checks if the whisper node is configured with the private key
// 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
// of the specified public pair. // of the specified public pair.
func (api *PublicWhisperAPI) HasKeyPair(id string) (bool, error) { func (api *PublicWhisperAPI) HasKeyPair(id string) (bool, error) {
if api.whisper == nil { if api.whisper == nil {
return false, whisperOffLineErr return false, whisperOfflineErr
} }
return api.whisper.HasKeyPair(id), nil 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) { func (api *PublicWhisperAPI) DeleteKeyPair(id string) (bool, error) {
if api.whisper == nil { if api.whisper == nil {
return false, whisperOffLineErr return false, whisperOfflineErr
} }
success := api.whisper.DeleteKeyPair(id) return api.whisper.DeleteKeyPair(id), nil
return success, nil
} }
// NewKeyPair generates a new cryptographic identity for the client, and injects // NewKeyPair generates a new cryptographic identity for the client, and injects
// it into the known identities for message decryption. // it into the known identities for message decryption.
func (api *PublicWhisperAPI) NewKeyPair() (string, error) { func (api *PublicWhisperAPI) NewKeyPair() (string, error) {
if api.whisper == nil { if api.whisper == nil {
return "", whisperOffLineErr return "", whisperOfflineErr
} }
return api.whisper.NewKeyPair() return api.whisper.NewKeyPair()
} }
// GetPublicKey returns the public key for identity id // 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 { if api.whisper == nil {
return "", whisperOffLineErr return nil, whisperOfflineErr
} }
key, err := api.whisper.GetPrivateKey(id) key, err := api.whisper.GetPrivateKey(id)
if err != nil { 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 // GetPrivateKey returns the private key for identity id
func (api *PublicWhisperAPI) GetPrivateKey(id string) (string, error) { func (api *PublicWhisperAPI) GetPrivateKey(id string) (string, error) {
if api.whisper == nil { if api.whisper == nil {
return "", whisperOffLineErr return "", whisperOfflineErr
} }
key, err := api.whisper.GetPrivateKey(id) key, err := api.whisper.GetPrivateKey(id)
if err != nil { if err != nil {
@ -163,57 +162,71 @@ func (api *PublicWhisperAPI) GetPrivateKey(id string) (string, error) {
return common.ToHex(crypto.FromECDSA(key)), nil 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. // which is then returned. Will be used in the future for session key exchange.
func (api *PublicWhisperAPI) GenerateSymmetricKey() (string, error) { func (api *PublicWhisperAPI) GenerateSymmetricKey() (string, error) {
if api.whisper == nil { if api.whisper == nil {
return "", whisperOffLineErr return "", whisperOfflineErr
} }
return api.whisper.GenerateSymKey() 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) { func (api *PublicWhisperAPI) AddSymmetricKeyDirect(key hexutil.Bytes) (string, error) {
if api.whisper == nil { if api.whisper == nil {
return "", whisperOffLineErr return "", whisperOfflineErr
} }
return api.whisper.AddSymKeyDirect(key) 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) { func (api *PublicWhisperAPI) AddSymmetricKeyFromPassword(password string) (string, error) {
if api.whisper == nil { if api.whisper == nil {
return "", whisperOffLineErr return "", whisperOfflineErr
} }
return api.whisper.AddSymKeyFromPassword(password) 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. // Otherwise returns false.
func (api *PublicWhisperAPI) HasSymmetricKey(id string) (bool, error) { func (api *PublicWhisperAPI) HasSymmetricKey(id string) (bool, error) {
if api.whisper == nil { if api.whisper == nil {
return false, whisperOffLineErr return false, whisperOfflineErr
} }
res := api.whisper.HasSymKey(id) res := api.whisper.HasSymKey(id)
return res, nil 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 { if api.whisper == nil {
return "", whisperOffLineErr return nil, whisperOfflineErr
} }
b, err := api.whisper.GetSymKey(name) b, err := api.whisper.GetSymKey(name)
if err != nil { 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) { func (api *PublicWhisperAPI) DeleteSymmetricKey(name string) (bool, error) {
if api.whisper == nil { if api.whisper == nil {
return false, whisperOffLineErr return false, whisperOfflineErr
} }
res := api.whisper.DeleteSymKey(name) res := api.whisper.DeleteSymKey(name)
return res, nil return res, nil
@ -223,7 +236,7 @@ func (api *PublicWhisperAPI) DeleteSymmetricKey(name string) (bool, error) {
// Returns the ID of the newly created filter. // Returns the ID of the newly created filter.
func (api *PublicWhisperAPI) Subscribe(args WhisperFilterArgs) (string, error) { func (api *PublicWhisperAPI) Subscribe(args WhisperFilterArgs) (string, error) {
if api.whisper == nil { if api.whisper == nil {
return "", whisperOffLineErr return "", whisperOfflineErr
} }
filter := Filter{ filter := Filter{
@ -233,61 +246,44 @@ func (api *PublicWhisperAPI) Subscribe(args WhisperFilterArgs) (string, error) {
AllowP2P: args.AllowP2P, AllowP2P: args.AllowP2P,
} }
var err error
for i, bt := range args.Topics { for i, bt := range args.Topics {
if len(bt) == 0 || len(bt) > 4 { if len(bt) == 0 || len(bt) > 4 {
info := fmt.Sprintf("Subscribe: topic %d has wrong size: %d", i, len(bt)) return "", errors.New(fmt.Sprintf("subscribe: topic %d has wrong size: %d", i, len(bt)))
log.Error(info)
return "", errors.New(info)
} }
filter.Topics = append(filter.Topics, bt) filter.Topics = append(filter.Topics, bt)
} }
err := ValidateKeyID(args.Key) if err = ValidateKeyID(args.Key); err != nil {
if err != nil { return "", errors.New("subscribe: " + err.Error())
info := "Subscribe: " + err.Error()
log.Error(info)
return "", errors.New(info)
} }
if len(args.SignedWith) > 0 { if len(args.SignedWith) > 0 {
if !ValidatePublicKey(filter.Src) { if !ValidatePublicKey(filter.Src) {
info := "Subscribe: Invalid 'SignedWith' field" return "", errors.New("subscribe: invalid 'SignedWith' field")
log.Error(info)
return "", errors.New(info)
} }
} }
if args.Symmetric { if args.Symmetric {
if len(args.Topics) == 0 { if len(args.Topics) == 0 {
info := "Subscribe: at least one topic must be specified with symmetric encryption" return "", errors.New("subscribe: at least one topic must be specified with symmetric encryption")
log.Error(info)
return "", errors.New(info)
} }
symKey, err := api.whisper.GetSymKey(args.Key) symKey, err := api.whisper.GetSymKey(args.Key)
if err != nil { if err != nil {
info := "Subscribe: invalid key ID" return "", errors.New("subscribe: invalid key ID")
log.Error(info)
return "", errors.New(info)
} }
if !validateSymmetricKey(symKey) { if !validateSymmetricKey(symKey) {
info := "Subscribe: retrieved key is invalid" return "", errors.New("subscribe: retrieved key is invalid")
log.Error(info)
return "", errors.New(info)
} }
filter.KeySym = symKey filter.KeySym = symKey
filter.SymKeyHash = crypto.Keccak256Hash(filter.KeySym) filter.SymKeyHash = crypto.Keccak256Hash(filter.KeySym)
} else { } else {
filter.KeyAsym, err = api.whisper.GetPrivateKey(args.Key) filter.KeyAsym, err = api.whisper.GetPrivateKey(args.Key)
if err != nil { if err != nil {
info := "Subscribe: invalid key ID" return "", errors.New("subscribe: invalid key ID")
log.Error(info)
return "", errors.New(info)
} }
if filter.KeyAsym == nil { if filter.KeyAsym == nil {
info := "Subscribe: non-existent identity provided" return "", errors.New("subscribe: non-existent identity provided")
log.Error(info)
return "", errors.New(info)
} }
} }
@ -299,7 +295,7 @@ func (api *PublicWhisperAPI) Unsubscribe(id string) {
api.whisper.Unsubscribe(id) 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 { func (api *PublicWhisperAPI) GetSubscriptionMessages(filterId string) []*WhisperMessage {
f := api.whisper.GetFilter(filterId) f := api.whisper.GetFilter(filterId)
if f != nil { if f != nil {
@ -309,7 +305,8 @@ func (api *PublicWhisperAPI) GetSubscriptionMessages(filterId string) []*Whisper
return toWhisperMessages(nil) 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 { func (api *PublicWhisperAPI) GetMessages(filterId string) []*WhisperMessage {
all := api.whisper.Messages(filterId) all := api.whisper.Messages(filterId)
return toWhisperMessages(all) 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. // Post creates a whisper message and injects it into the network for distribution.
func (api *PublicWhisperAPI) Post(args PostArgs) error { func (api *PublicWhisperAPI) Post(args PostArgs) error {
if api.whisper == nil { if api.whisper == nil {
return whisperOffLineErr return whisperOfflineErr
} }
var err error var err error
@ -340,96 +337,69 @@ func (api *PublicWhisperAPI) Post(args PostArgs) error {
} }
if len(args.Key) == 0 { if len(args.Key) == 0 {
info := "Post: key is missing" return errors.New("post: key is missing")
log.Error(info)
return errors.New(info)
} }
if len(args.SignWith) > 0 { if len(args.SignWith) > 0 {
params.Src, err = api.whisper.GetPrivateKey(args.SignWith) params.Src, err = api.whisper.GetPrivateKey(args.SignWith)
if err != nil { if err != nil {
log.Error(err.Error())
return err return err
} }
if params.Src == nil { if params.Src == nil {
info := "Post: empty identity" return errors.New("post: empty identity")
log.Error(info)
return errors.New(info)
} }
} }
if len(args.Topic) == TopicLength { if len(args.Topic) == TopicLength {
params.Topic = BytesToTopic(args.Topic) params.Topic = BytesToTopic(args.Topic)
} else if len(args.Topic) != 0 { } else if len(args.Topic) != 0 {
info := fmt.Sprintf("Post: wrong topic size %d", len(args.Topic)) return errors.New(fmt.Sprintf("post: wrong topic size %d", len(args.Topic)))
log.Error(info)
return errors.New(info)
} }
if args.Type == "sym" { if args.Type == "sym" {
err = ValidateKeyID(args.Key) if err = ValidateKeyID(args.Key); err != nil {
if err != nil {
log.Error(err.Error())
return err return err
} }
params.KeySym, err = api.whisper.GetSymKey(args.Key) params.KeySym, err = api.whisper.GetSymKey(args.Key)
if err != nil { if err != nil {
log.Error(err.Error())
return err return err
} }
if !validateSymmetricKey(params.KeySym) { if !validateSymmetricKey(params.KeySym) {
info := "Post: key for symmetric encryption is invalid" return errors.New("post: key for symmetric encryption is invalid")
log.Error(info)
return errors.New(info)
} }
if len(params.Topic) == 0 { if len(params.Topic) == 0 {
info := "Post: topic is missing for symmetric encryption" return errors.New("post: topic is missing for symmetric encryption")
log.Error(info)
return errors.New(info)
} }
} else if args.Type == "asym" { } else if args.Type == "asym" {
params.Dst = crypto.ToECDSAPub(common.FromHex(args.Key)) params.Dst = crypto.ToECDSAPub(common.FromHex(args.Key))
if !ValidatePublicKey(params.Dst) { if !ValidatePublicKey(params.Dst) {
info := "Post: public key for asymmetric encryption is invalid" return errors.New("post: public key for asymmetric encryption is invalid")
log.Error(info)
return errors.New(info)
} }
} else { } else {
info := "Post: wrong type (sym/asym)" return errors.New("post: wrong type (sym/asym)")
log.Error(info)
return errors.New(info)
} }
// encrypt and send // encrypt and send
message := NewSentMessage(&params) message := NewSentMessage(&params)
if message == nil { if message == nil {
info := "Post: failed create new message, probably due to failed rand function (OS level)" return errors.New("post: failed create new message, probably due to failed rand function (OS level)")
log.Error(info)
return errors.New(info)
} }
envelope, err := message.Wrap(&params) envelope, err := message.Wrap(&params)
if err != nil { if err != nil {
log.Error(err.Error())
return err return err
} }
if envelope.size() > api.whisper.maxMsgLength { if envelope.size() > api.whisper.maxMsgLength {
info := "Post: message is too big" return errors.New("post: message is too big")
log.Error(info)
return errors.New(info)
} }
if len(args.TargetPeer) != 0 { if len(args.TargetPeer) != 0 {
n, err := discover.ParseNode(args.TargetPeer) n, err := discover.ParseNode(args.TargetPeer)
if err != nil { if err != nil {
info := "Post: failed to parse enode of target peer: " + err.Error() return errors.New("post: failed to parse enode of target peer: " + err.Error())
log.Error(info)
return errors.New(info)
} }
return api.whisper.SendP2PMessage(n.ID[:], envelope) return api.whisper.SendP2PMessage(n.ID[:], envelope)
} else if args.PowTarget < api.whisper.minPoW { } else if args.PowTarget < api.whisper.minPoW {
info := "Post: target PoW is less than minimum PoW, the message can not be sent" return errors.New("post: target PoW is less than minimum PoW, the message can not be sent")
log.Error(info)
return errors.New(info)
} }
return api.whisper.Send(envelope) return api.whisper.Send(envelope)
@ -473,12 +443,13 @@ func (args *WhisperFilterArgs) UnmarshalJSON(b []byte) (err error) {
return err return err
} }
if obj.Type == "sym" { switch obj.Type {
case "sym":
args.Symmetric = true args.Symmetric = true
} else if obj.Type == "asym" { case "asym":
args.Symmetric = false args.Symmetric = false
} else { default:
return fmt.Errorf("Wrong type (sym/asym") return errors.New("wrong type (sym/asym")
} }
args.Key = obj.Key args.Key = obj.Key

View file

@ -94,11 +94,11 @@ func (fs *Filters) NotifyWatchers(env *Envelope, p2pMessage bool) {
fs.mutex.RLock() fs.mutex.RLock()
defer fs.mutex.RUnlock() defer fs.mutex.RUnlock()
j := -1 i := -1 // only used for logging info
for _, watcher := range fs.watchers { for _, watcher := range fs.watchers {
j++ i++
if p2pMessage && !watcher.AllowP2P { 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 continue
} }
@ -110,10 +110,10 @@ func (fs *Filters) NotifyWatchers(env *Envelope, p2pMessage bool) {
if match { if match {
msg = env.Open(watcher) msg = env.Open(watcher)
if msg == nil { 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 { } 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 { for _, bt := range f.Topics {
if MatchSingleTopic(topic, bt) { if matchSingleTopic(topic, bt) {
return true return true
} }
} }
return false return false
} }
func MatchSingleTopic(topic TopicType, bt []byte) bool { func matchSingleTopic(topic TopicType, bt []byte) bool {
if len(bt) > 4 { if len(bt) > 4 {
bt = bt[0:4] bt = bt[:4]
} }
for j, b := range bt { for j, b := range bt {

View file

@ -26,7 +26,6 @@ import (
"crypto/sha256" "crypto/sha256"
"errors" "errors"
"fmt" "fmt"
mrand "math/rand"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto" "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") panic("please fix the padding algorithm before releasing new version")
} }
buf := make([]byte, padSize) buf := make([]byte, padSize)
_, err := mrand.Read(buf[1:]) _, err := crand.Read(buf[1:])
if err != nil { if err != nil {
return err return err
} }

View file

@ -119,8 +119,10 @@ func initialize(t *testing.T) {
topics := make([]TopicType, 0) topics := make([]TopicType, 0)
topics = append(topics, sharedTopic) topics = append(topics, sharedTopic)
f := Filter{KeySym: sharedKey} f := Filter{KeySym: sharedKey}
f.Topics = make([][]byte, 1) f.Topics = [][]byte{topics[0][:]}
f.Topics[0] = topics[0][:] // todo: delete if pass
//f.Topics = make([][]byte, 1)
//f.Topics[0] = topics[0][:]
node.filerId, err = node.shh.Watch(&f) node.filerId, err = node.shh.Watch(&f)
if err != nil { if err != nil {
t.Fatalf("failed to install the filter: %s.", err) t.Fatalf("failed to install the filter: %s.", err)

View file

@ -46,12 +46,12 @@ type Statistics struct {
// Whisper represents a dark communication interface through the Ethereum // Whisper represents a dark communication interface through the Ethereum
// network, using its very own P2P communication layer. // network, using its very own P2P communication layer.
type Whisper struct { type Whisper struct {
protocol p2p.Protocol protocol p2p.Protocol // Protocol description and parameters
filters *Filters filters *Filters // Message filters installed with Subscribe function
privateKeys map[string]*ecdsa.PrivateKey privateKeys map[string]*ecdsa.PrivateKey // Private key storage
symKeys map[string][]byte symKeys map[string][]byte // Symmetric key storage
keyMu sync.RWMutex keyMu sync.RWMutex // Mutex associated with key storages
envelopes map[common.Hash]*Envelope // Pool of envelopes currently tracked by this node envelopes map[common.Hash]*Envelope // Pool of envelopes currently tracked by this node
expirations map[uint32]*set.SetNonTS // Message expiration pool expirations map[uint32]*set.SetNonTS // Message expiration pool
@ -60,17 +60,17 @@ type Whisper struct {
peers map[*Peer]struct{} // Set of currently active peers peers map[*Peer]struct{} // Set of currently active peers
peerMu sync.RWMutex // Mutex to sync the active peer set 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 minPoW float64 // Minimal PoW required by the whisper node
p2pMsgQueue chan *Envelope maxMsgLength int // Maximal message length allowed by the whisper node
quit chan struct{} overflow bool // Indicator of message queue overflow
stats Statistics stats Statistics // Statistics of whisper node
minPoW float64 mailServer MailServer // MailServer interface
maxMsgLength int
overflow bool
} }
// New creates a Whisper client ready to communicate through the Ethereum P2P network. // 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) { func (w *Whisper) RegisterServer(server MailServer) {
w.mailServer = server w.mailServer = server
} }
@ -127,6 +129,7 @@ func (w *Whisper) Version() uint {
return w.protocol.Version return w.protocol.Version
} }
// SetMaxMessageLength sets the maximal message length allowed by this node
func (w *Whisper) SetMaxMessageLength(val int) error { func (w *Whisper) SetMaxMessageLength(val int) error {
if val <= 0 { if val <= 0 {
return fmt.Errorf("Invalid message length: %d", val) return fmt.Errorf("Invalid message length: %d", val)
@ -135,6 +138,7 @@ func (w *Whisper) SetMaxMessageLength(val int) error {
return nil return nil
} }
// SetMinimumPoW sets the minimal PoW required by this node
func (w *Whisper) SetMinimumPoW(val float64) error { func (w *Whisper) SetMinimumPoW(val float64) error {
if val <= 0.0 { if val <= 0.0 {
return fmt.Errorf("Invalid PoW: %f", val) return fmt.Errorf("Invalid PoW: %f", val)
@ -166,6 +170,11 @@ func (w *Whisper) AllowP2PMessagesFromPeer(peerID []byte) error {
return nil 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 { func (w *Whisper) RequestHistoricMessages(peerID []byte, envelope *Envelope) error {
p, err := w.getPeer(peerID) p, err := w.getPeer(peerID)
if err != nil { if err != nil {
@ -175,14 +184,17 @@ func (w *Whisper) RequestHistoricMessages(peerID []byte, envelope *Envelope) err
return p2p.Send(p.ws, p2pRequestCode, envelope) 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 { func (w *Whisper) SendP2PMessage(peerID []byte, envelope *Envelope) error {
p, err := w.getPeer(peerID) p, err := w.getPeer(peerID)
if err != nil { if err != nil {
return err 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 { func (w *Whisper) SendP2PDirect(peer *Peer, envelope *Envelope) error {
return p2p.Send(peer.ws, p2pCode, envelope) return p2p.Send(peer.ws, p2pCode, envelope)
} }
@ -247,6 +259,8 @@ func (w *Whisper) GetPrivateKey(id string) (*ecdsa.PrivateKey, error) {
return key, nil 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) { func (w *Whisper) GenerateSymKey() (string, error) {
const size = aesKeyLength * 2 const size = aesKeyLength * 2
buf := make([]byte, size) buf := make([]byte, size)
@ -281,6 +295,7 @@ func (w *Whisper) GenerateSymKey() (string, error) {
return id, nil return id, nil
} }
// AddSymKeyDirect stores the key, and returns its id.
func (w *Whisper) AddSymKeyDirect(key []byte) (string, error) { func (w *Whisper) AddSymKeyDirect(key []byte) (string, error) {
if len(key) != aesKeyLength { if len(key) != aesKeyLength {
return "", fmt.Errorf("Wrong key size: %d", len(key)) return "", fmt.Errorf("Wrong key size: %d", len(key))
@ -301,6 +316,7 @@ func (w *Whisper) AddSymKeyDirect(key []byte) (string, error) {
return id, nil return id, nil
} }
// AddSymKeyFromPassword generates the key from password, stores it, and returns its id.
func (w *Whisper) AddSymKeyFromPassword(password string) (string, error) { func (w *Whisper) AddSymKeyFromPassword(password string) (string, error) {
id, err := GenerateRandomID() id, err := GenerateRandomID()
if err != nil { if err != nil {
@ -326,12 +342,15 @@ func (w *Whisper) AddSymKeyFromPassword(password string) (string, error) {
return id, nil 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 { func (w *Whisper) HasSymKey(id string) bool {
w.keyMu.RLock() w.keyMu.RLock()
defer w.keyMu.RUnlock() defer w.keyMu.RUnlock()
return w.symKeys[id] != nil return w.symKeys[id] != nil
} }
// DeleteSymKey deletes the key associated with the name string if it exists.
func (w *Whisper) DeleteSymKey(id string) bool { func (w *Whisper) DeleteSymKey(id string) bool {
w.keyMu.Lock() w.keyMu.Lock()
defer w.keyMu.Unlock() defer w.keyMu.Unlock()
@ -342,6 +361,7 @@ func (w *Whisper) DeleteSymKey(id string) bool {
return false return false
} }
// GetSymKey returns the symmetric key associated with the given id.
func (w *Whisper) GetSymKey(id string) ([]byte, error) { func (w *Whisper) GetSymKey(id string) ([]byte, error) {
w.keyMu.RLock() w.keyMu.RLock()
defer w.keyMu.RUnlock() defer w.keyMu.RUnlock()
@ -351,12 +371,13 @@ func (w *Whisper) GetSymKey(id string) ([]byte, error) {
return nil, fmt.Errorf("non-existent key ID") return nil, fmt.Errorf("non-existent key ID")
} }
// Watch installs a new message handler to run in case a matching packet arrives // Watch installs a new message handler used for filtering, decrypting
// from the whisper network. // and subsequent storing of incoming messages.
func (w *Whisper) Watch(f *Filter) (string, error) { func (w *Whisper) Watch(f *Filter) (string, error) {
return w.filters.Install(f) return w.filters.Install(f)
} }
// GetFilter returns the filter by id.
func (w *Whisper) GetFilter(id string) *Filter { func (w *Whisper) GetFilter(id string) *Filter {
return w.filters.Get(id) return w.filters.Get(id)
} }
@ -662,6 +683,7 @@ func (w *Whisper) expire() {
} }
} }
// Stats returns the whisper node statistics.
func (w *Whisper) Stats() string { func (w *Whisper) Stats() string {
result := fmt.Sprintf("Memory usage: %d bytes. Average messages cleared per expiry cycle: %d. Total messages cleared: %d.", 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) 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).", result += fmt.Sprintf(" Latest expiry cycle cleared %d messages (%d bytes).",
w.stats.messagesCleared, w.stats.memoryCleared) w.stats.messagesCleared, w.stats.memoryCleared)
} }
if w.overflow {
result += " Message queue state: overflow."
}
return result return result
} }
@ -702,6 +727,7 @@ func (w *Whisper) Messages(id string) []*ReceivedMessage {
return result return result
} }
// isEnvelopeCached checks if envelope with specific hash has already been received and cached.
func (w *Whisper) isEnvelopeCached(hash common.Hash) bool { func (w *Whisper) isEnvelopeCached(hash common.Hash) bool {
w.poolMu.Lock() w.poolMu.Lock()
defer w.poolMu.Unlock() defer w.poolMu.Unlock()
@ -710,6 +736,7 @@ func (w *Whisper) isEnvelopeCached(hash common.Hash) bool {
return exist return exist
} }
// reset resets the node's statistics after each expiry cycle.
func (s *Statistics) reset() { func (s *Statistics) reset() {
s.cycles++ s.cycles++
s.totalMessagesCleared += s.messagesCleared s.totalMessagesCleared += s.messagesCleared
@ -718,6 +745,7 @@ func (s *Statistics) reset() {
s.messagesCleared = 0 s.messagesCleared = 0
} }
// ValidateKeyID checks the format of key id.
func ValidateKeyID(id string) error { func ValidateKeyID(id string) error {
const target = keyIdSize * 2 const target = keyIdSize * 2
if len(id) != target { if len(id) != target {
@ -726,10 +754,12 @@ func ValidateKeyID(id string) error {
return nil return nil
} }
// ValidatePublicKey checks the format of the given public key.
func ValidatePublicKey(k *ecdsa.PublicKey) bool { func ValidatePublicKey(k *ecdsa.PublicKey) bool {
return k != nil && k.X != nil && k.Y != nil && k.X.Sign() != 0 && k.Y.Sign() != 0 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 { func validatePrivateKey(k *ecdsa.PrivateKey) bool {
if k == nil || k.D == nil || k.D.Sign() == 0 { if k == nil || k.D == nil || k.D.Sign() == 0 {
return false return false
@ -742,6 +772,7 @@ func validateSymmetricKey(k []byte) bool {
return len(k) > 0 && !containsOnlyZeros(k) return len(k) > 0 && !containsOnlyZeros(k)
} }
// containsOnlyZeros checks if the data contain only zeros.
func containsOnlyZeros(data []byte) bool { func containsOnlyZeros(data []byte) bool {
for _, b := range data { for _, b := range data {
if b != 0 { if b != 0 {
@ -751,6 +782,7 @@ func containsOnlyZeros(data []byte) bool {
return true return true
} }
// bytesToIntLittleEndian converts the slice to 64-bit unsigned integer.
func bytesToIntLittleEndian(b []byte) (res uint64) { func bytesToIntLittleEndian(b []byte) (res uint64) {
mul := uint64(1) mul := uint64(1)
for i := 0; i < len(b); i++ { for i := 0; i < len(b); i++ {
@ -760,6 +792,7 @@ func bytesToIntLittleEndian(b []byte) (res uint64) {
return res return res
} }
// BytesToIntBigEndian converts the slice to 64-bit unsigned integer.
func BytesToIntBigEndian(b []byte) (res uint64) { func BytesToIntBigEndian(b []byte) (res uint64) {
for i := 0; i < len(b); i++ { for i := 0; i < len(b); i++ {
res *= 256 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) { func GenerateRandomID() (id string, err error) {
buf := make([]byte, keyIdSize) buf := make([]byte, keyIdSize)
_, err = crand.Read(buf) _, err = crand.Read(buf)