whisper: fixed broadcast race

Whisper's expire and broadcast loops happen in two separate go routines.
Whenever an envelope is being expired it's removed from the set of
envelopes and it looses all information about the envelope, including
the "known hash". After the envelope has been removed it can be
re-accepted by a broadcasting peer putting back the envelope in the set
of envelopes. Since the envelope broadcast loop is separate of the
expire loop expired messages may be broadcast to other peer, resulting
in messages **never** being dropped.

This PR includes an expire check before adding new messages to the set
of envelopes.
This commit is contained in:
Jeffrey Wilcke 2015-11-20 19:23:55 +01:00
parent 4c2933ad82
commit 5f0a4416db
3 changed files with 19 additions and 2 deletions

View File

@ -239,14 +239,17 @@ func TestPeerMessageExpiration(t *testing.T) {
}
payload := []interface{}{envelope}
if err := p2p.ExpectMsg(tester.stream, messagesCode, payload); err != nil {
t.Fatalf("message mismatch: %v", err)
// A premature empty message may have been broadcast, check the next too
if err := p2p.ExpectMsg(tester.stream, messagesCode, payload); err != nil {
t.Fatalf("message mismatch: %v", err)
}
}
// Check that the message is inside the cache
if !peer.known.Has(envelope.Hash()) {
t.Fatalf("message not found in cache")
}
// Discard messages until expiration and check cache again
exp := time.Now().Add(time.Second + expirationCycle)
exp := time.Now().Add(time.Second + 2*expirationCycle + 100*time.Millisecond)
for time.Now().Before(exp) {
if err := p2p.ExpectMsg(tester.stream, messagesCode, []interface{}{}); err != nil {
t.Fatalf("message mismatch: %v", err)

View File

@ -234,6 +234,11 @@ func (self *Whisper) add(envelope *Envelope) error {
self.poolMu.Lock()
defer self.poolMu.Unlock()
// short circuit when a received envelope has already expired
if envelope.Expiry <= uint32(time.Now().Unix()) {
return nil
}
// Insert the message into the tracked pool
hash := envelope.Hash()
if _, ok := self.messages[hash]; ok {

View File

@ -207,4 +207,13 @@ func TestMessageExpiration(t *testing.T) {
if found {
t.Fatalf("message not expired from cache")
}
node.add(envelope)
node.poolMu.RLock()
_, found = node.messages[envelope.Hash()]
node.poolMu.RUnlock()
if found {
t.Fatalf("message was added to cache")
}
}