|
|
|
@ -22,6 +22,7 @@ import ( |
|
|
|
|
crand "crypto/rand" |
|
|
|
|
"crypto/sha256" |
|
|
|
|
"fmt" |
|
|
|
|
"runtime" |
|
|
|
|
"sync" |
|
|
|
|
"time" |
|
|
|
|
|
|
|
|
@ -45,7 +46,7 @@ type Whisper struct { |
|
|
|
|
symKeys map[string][]byte |
|
|
|
|
keyMu sync.RWMutex |
|
|
|
|
|
|
|
|
|
envelopes map[common.Hash]*Envelope // Pool of messages currently tracked by this node
|
|
|
|
|
envelopes map[common.Hash]*Envelope // Pool of envelopes currently tracked by this node
|
|
|
|
|
messages map[common.Hash]*ReceivedMessage // Pool of successfully decrypted messages, which are not expired yet
|
|
|
|
|
expirations map[uint32]*set.SetNonTS // Message expiration pool
|
|
|
|
|
poolMu sync.RWMutex // Mutex to sync the message and expiration pools
|
|
|
|
@ -55,22 +56,28 @@ type Whisper struct { |
|
|
|
|
|
|
|
|
|
mailServer MailServer |
|
|
|
|
|
|
|
|
|
quit chan struct{} |
|
|
|
|
test bool |
|
|
|
|
messageQueue chan *Envelope |
|
|
|
|
p2pMsgQueue chan *Envelope |
|
|
|
|
quit chan struct{} |
|
|
|
|
|
|
|
|
|
overflow bool |
|
|
|
|
test bool |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// New creates a Whisper client ready to communicate through the Ethereum P2P network.
|
|
|
|
|
// Param s should be passed if you want to implement mail server, otherwise nil.
|
|
|
|
|
func NewWhisper(server MailServer) *Whisper { |
|
|
|
|
whisper := &Whisper{ |
|
|
|
|
privateKeys: make(map[string]*ecdsa.PrivateKey), |
|
|
|
|
symKeys: make(map[string][]byte), |
|
|
|
|
envelopes: make(map[common.Hash]*Envelope), |
|
|
|
|
messages: make(map[common.Hash]*ReceivedMessage), |
|
|
|
|
expirations: make(map[uint32]*set.SetNonTS), |
|
|
|
|
peers: make(map[*Peer]struct{}), |
|
|
|
|
mailServer: server, |
|
|
|
|
quit: make(chan struct{}), |
|
|
|
|
privateKeys: make(map[string]*ecdsa.PrivateKey), |
|
|
|
|
symKeys: make(map[string][]byte), |
|
|
|
|
envelopes: make(map[common.Hash]*Envelope), |
|
|
|
|
messages: make(map[common.Hash]*ReceivedMessage), |
|
|
|
|
expirations: make(map[uint32]*set.SetNonTS), |
|
|
|
|
peers: make(map[*Peer]struct{}), |
|
|
|
|
mailServer: server, |
|
|
|
|
messageQueue: make(chan *Envelope, messageQueueLimit), |
|
|
|
|
p2pMsgQueue: make(chan *Envelope, messageQueueLimit), |
|
|
|
|
quit: make(chan struct{}), |
|
|
|
|
} |
|
|
|
|
whisper.filters = NewFilters(whisper) |
|
|
|
|
|
|
|
|
@ -124,7 +131,7 @@ func (w *Whisper) RequestHistoricMessages(peerID []byte, data []byte) error { |
|
|
|
|
return err |
|
|
|
|
} |
|
|
|
|
p.trusted = true |
|
|
|
|
return p2p.Send(p.ws, mailRequestCode, data) |
|
|
|
|
return p2p.Send(p.ws, p2pRequestCode, data) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (w *Whisper) SendP2PMessage(peerID []byte, envelope *Envelope) error { |
|
|
|
@ -270,6 +277,12 @@ func (w *Whisper) Send(envelope *Envelope) error { |
|
|
|
|
func (w *Whisper) Start(*p2p.Server) error { |
|
|
|
|
glog.V(logger.Info).Infoln("Whisper started") |
|
|
|
|
go w.update() |
|
|
|
|
|
|
|
|
|
numCPU := runtime.NumCPU() |
|
|
|
|
for i := 0; i < numCPU; i++ { |
|
|
|
|
go w.processQueue() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return nil |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -350,10 +363,10 @@ func (wh *Whisper) runMessageLoop(p *Peer, rw p2p.MsgReadWriter) error { |
|
|
|
|
return fmt.Errorf("garbage received (directMessage)") |
|
|
|
|
} |
|
|
|
|
for _, envelope := range envelopes { |
|
|
|
|
wh.postEvent(envelope, p2pCode) |
|
|
|
|
wh.postEvent(envelope, true) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
case mailRequestCode: |
|
|
|
|
case p2pRequestCode: |
|
|
|
|
// Must be processed if mail server is implemented. Otherwise ignore.
|
|
|
|
|
if wh.mailServer != nil { |
|
|
|
|
s := rlp.NewStream(packet.Payload, uint64(packet.Size)) |
|
|
|
@ -382,7 +395,7 @@ func (wh *Whisper) add(envelope *Envelope) error { |
|
|
|
|
|
|
|
|
|
if sent > now { |
|
|
|
|
if sent-SynchAllowance > now { |
|
|
|
|
return fmt.Errorf("message created in the future") |
|
|
|
|
return fmt.Errorf("envelope created in the future [%x]", envelope.Hash()) |
|
|
|
|
} else { |
|
|
|
|
// recalculate PoW, adjusted for the time difference, plus one second for latency
|
|
|
|
|
envelope.calculatePoW(sent - now + 1) |
|
|
|
@ -393,30 +406,31 @@ func (wh *Whisper) add(envelope *Envelope) error { |
|
|
|
|
if envelope.Expiry+SynchAllowance*2 < now { |
|
|
|
|
return fmt.Errorf("very old message") |
|
|
|
|
} else { |
|
|
|
|
glog.V(logger.Debug).Infof("expired envelope dropped [%x]", envelope.Hash()) |
|
|
|
|
return nil // drop envelope without error
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if len(envelope.Data) > MaxMessageLength { |
|
|
|
|
return fmt.Errorf("huge messages are not allowed") |
|
|
|
|
return fmt.Errorf("huge messages are not allowed [%x]", envelope.Hash()) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if len(envelope.Version) > 4 { |
|
|
|
|
return fmt.Errorf("oversized Version") |
|
|
|
|
return fmt.Errorf("oversized version [%x]", envelope.Hash()) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if len(envelope.AESNonce) > AESNonceMaxLength { |
|
|
|
|
// the standard AES GSM nonce size is 12,
|
|
|
|
|
// but const gcmStandardNonceSize cannot be accessed directly
|
|
|
|
|
return fmt.Errorf("oversized AESNonce") |
|
|
|
|
return fmt.Errorf("oversized AESNonce [%x]", envelope.Hash()) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if len(envelope.Salt) > saltLength { |
|
|
|
|
return fmt.Errorf("oversized Salt") |
|
|
|
|
return fmt.Errorf("oversized salt [%x]", envelope.Hash()) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if envelope.PoW() < MinimumPoW && !wh.test { |
|
|
|
|
glog.V(logger.Debug).Infof("envelope with low PoW dropped: %f", envelope.PoW()) |
|
|
|
|
glog.V(logger.Debug).Infof("envelope with low PoW dropped: %f [%x]", envelope.PoW(), envelope.Hash()) |
|
|
|
|
return nil // drop envelope without error
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -436,22 +450,59 @@ func (wh *Whisper) add(envelope *Envelope) error { |
|
|
|
|
wh.poolMu.Unlock() |
|
|
|
|
|
|
|
|
|
if alreadyCached { |
|
|
|
|
glog.V(logger.Detail).Infof("whisper envelope already cached: %x\n", envelope) |
|
|
|
|
glog.V(logger.Detail).Infof("whisper envelope already cached [%x]\n", envelope.Hash()) |
|
|
|
|
} else { |
|
|
|
|
wh.postEvent(envelope, messagesCode) // notify the local node about the new message
|
|
|
|
|
glog.V(logger.Detail).Infof("cached whisper envelope %v\n", envelope) |
|
|
|
|
glog.V(logger.Detail).Infof("cached whisper envelope [%x]: %v\n", envelope.Hash(), envelope) |
|
|
|
|
wh.postEvent(envelope, false) // notify the local node about the new message
|
|
|
|
|
} |
|
|
|
|
return nil |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// postEvent delivers the message to the watchers.
|
|
|
|
|
func (w *Whisper) postEvent(envelope *Envelope, messageCode uint64) { |
|
|
|
|
// postEvent queues the message for further processing.
|
|
|
|
|
func (w *Whisper) postEvent(envelope *Envelope, isP2P bool) { |
|
|
|
|
// if the version of incoming message is higher than
|
|
|
|
|
// currently supported version, we can not decrypt it,
|
|
|
|
|
// and therefore just ignore this message
|
|
|
|
|
if envelope.Ver() <= EnvelopeVersion { |
|
|
|
|
// todo: review if you need an additional thread here
|
|
|
|
|
go w.filters.NotifyWatchers(envelope, messageCode) |
|
|
|
|
if isP2P { |
|
|
|
|
w.p2pMsgQueue <- envelope |
|
|
|
|
} else { |
|
|
|
|
w.checkOverflow() |
|
|
|
|
w.messageQueue <- envelope |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// checkOverflow checks if message queue overflow occurs and reports it if necessary.
|
|
|
|
|
func (w *Whisper) checkOverflow() { |
|
|
|
|
queueSize := len(w.messageQueue) |
|
|
|
|
|
|
|
|
|
if queueSize == messageQueueLimit { |
|
|
|
|
if !w.overflow { |
|
|
|
|
w.overflow = true |
|
|
|
|
glog.V(logger.Warn).Infoln("message queue overflow") |
|
|
|
|
} |
|
|
|
|
} else if queueSize <= messageQueueLimit/2 { |
|
|
|
|
if w.overflow { |
|
|
|
|
w.overflow = false |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// processQueue delivers the messages to the watchers during the lifetime of the whisper node.
|
|
|
|
|
func (w *Whisper) processQueue() { |
|
|
|
|
var e *Envelope |
|
|
|
|
for { |
|
|
|
|
select { |
|
|
|
|
case <-w.quit: |
|
|
|
|
return |
|
|
|
|
|
|
|
|
|
case e = <-w.messageQueue: |
|
|
|
|
w.filters.NotifyWatchers(e, false) |
|
|
|
|
|
|
|
|
|
case e = <-w.p2pMsgQueue: |
|
|
|
|
w.filters.NotifyWatchers(e, true) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|