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()
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)

View file

@ -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(&params)
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(&params)
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

View file

@ -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 {

View file

@ -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
}

View file

@ -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)

View file

@ -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)