swarm/pss: Create outbox queue for pss

Implements a queue manager to enable resending when forwarding fails.
Messages are not forwarded right away, but put in a queue which is in
turn fetched by a loop started when the service starts.

swarm/pss: WIP outbox

swarm/pss: Add read mutexes

swarm/pss: Implement queue as channel

swarm/pss: Remove commented code
This commit is contained in:
lash 2018-03-29 20:55:41 +02:00
parent 17977c2e79
commit 8645ead841
3 changed files with 62 additions and 47 deletions

View file

@ -307,7 +307,7 @@ func (self *HandshakeController) handleKeys(pubkeyid string, keymsg *handshakeMs
copy(sendsymkey, key) copy(sendsymkey, key)
var address PssAddress var address PssAddress
copy(address[:], keymsg.From) copy(address[:], keymsg.From)
sendsymkeyid, err := self.pss.SetSymmetricKey(sendsymkey, keymsg.Topic, &address, false) sendsymkeyid, err := self.pss.setSymmetricKey(sendsymkey, keymsg.Topic, &address, false, false)
if err != nil { if err != nil {
return err return err
} }

View file

@ -4,7 +4,6 @@ import (
"bytes" "bytes"
"crypto/ecdsa" "crypto/ecdsa"
"crypto/rand" "crypto/rand"
"errors"
"fmt" "fmt"
"sync" "sync"
"time" "time"
@ -31,7 +30,9 @@ const (
defaultWhisperWorkTime = 3 defaultWhisperWorkTime = 3
defaultWhisperPoW = 0.0000000001 defaultWhisperPoW = 0.0000000001
defaultMaxMsgSize = 1024 * 1024 defaultMaxMsgSize = 1024 * 1024
defaultCleanInterval = 1000 * 60 * 10 defaultCleanInterval = time.Second * 60 * 10
defaultDequeueInterval = time.Millisecond * 10
defaultOutboxQueueSize = 10000
pssProtocolName = "pss" pssProtocolName = "pss"
pssVersion = 1 pssVersion = 1
) )
@ -94,13 +95,14 @@ type Pss struct {
// sending and forwarding // sending and forwarding
fwdPool map[string]*protocols.Peer // keep track of all peers sitting on the pssmsg routing layer fwdPool map[string]*protocols.Peer // keep track of all peers sitting on the pssmsg routing layer
fwdPoolMu sync.Mutex fwdPoolMu sync.RWMutex
fwdCache map[pssDigest]pssCacheEntry // checksum of unique fields from pssmsg mapped to expiry, cache to determine whether to drop msg fwdCache map[pssDigest]pssCacheEntry // checksum of unique fields from pssmsg mapped to expiry, cache to determine whether to drop msg
fwdCacheMu sync.Mutex fwdCacheMu sync.RWMutex
cacheTTL time.Duration // how long to keep messages in fwdCache (not implemented) cacheTTL time.Duration // how long to keep messages in fwdCache (not implemented)
msgTTL time.Duration msgTTL time.Duration
paddingByteSize int paddingByteSize int
capstring string capstring string
outbox chan *PssMsg
// keys and peers // keys and peers
pubKeyPool map[string]map[Topic]*pssPeer // mapping of hex public keys to peer address by topic. pubKeyPool map[string]map[Topic]*pssPeer // mapping of hex public keys to peer address by topic.
@ -113,7 +115,7 @@ type Pss struct {
// message handling // message handling
handlers map[Topic]map[*Handler]bool // topic and version based pss payload handlers. See pss.Handle() handlers map[Topic]map[*Handler]bool // topic and version based pss payload handlers. See pss.Handle()
handlersMu sync.Mutex handlersMu sync.RWMutex
// process // process
quitC chan struct{} quitC chan struct{}
@ -145,6 +147,7 @@ func NewPss(k network.Overlay, dpa *storage.DPA, params *PssParams) *Pss {
msgTTL: params.MsgTTL, msgTTL: params.MsgTTL,
paddingByteSize: defaultPaddingByteSize, paddingByteSize: defaultPaddingByteSize,
capstring: cap.String(), capstring: cap.String(),
outbox: make(chan *PssMsg, defaultOutboxQueueSize),
pubKeyPool: make(map[string]map[Topic]*pssPeer), pubKeyPool: make(map[string]map[Topic]*pssPeer),
symKeyPool: make(map[string]map[Topic]*pssPeer), symKeyPool: make(map[string]map[Topic]*pssPeer),
@ -161,12 +164,24 @@ func NewPss(k network.Overlay, dpa *storage.DPA, params *PssParams) *Pss {
func (self *Pss) Start(srv *p2p.Server) error { func (self *Pss) Start(srv *p2p.Server) error {
go func() { go func() {
tickC := time.Tick(defaultCleanInterval) for {
select { tickC := time.Tick(defaultCleanInterval)
case <-tickC: select {
self.cleanKeys() case <-tickC:
case <-self.quitC: self.cleanKeys()
log.Info("pss shutting down") case <-self.quitC:
log.Info("pss shutting down")
}
}
}()
go func() {
for {
select {
case msg := <-self.outbox:
self.forward(msg)
case <-self.quitC:
log.Info("pss shutting down")
}
} }
}() }()
log.Debug("Started pss", "public key", common.ToHex(crypto.FromECDSAPub(self.PublicKey()))) log.Debug("Started pss", "public key", common.ToHex(crypto.FromECDSAPub(self.PublicKey())))
@ -272,8 +287,8 @@ func (self *Pss) deregister(topic *Topic, h *Handler) {
// get all registered handlers for respective topics // get all registered handlers for respective topics
func (self *Pss) getHandlers(topic Topic) map[*Handler]bool { func (self *Pss) getHandlers(topic Topic) map[*Handler]bool {
self.handlersMu.Lock() self.handlersMu.RLock()
defer self.handlersMu.Unlock() defer self.handlersMu.RUnlock()
return self.handlers[topic] return self.handlers[topic]
} }
@ -286,20 +301,13 @@ func (self *Pss) handlePssMsg(msg interface{}) error {
if ok { if ok {
var err error var err error
if !self.isSelfPossibleRecipient(pssmsg) { if !self.isSelfPossibleRecipient(pssmsg) {
msgexp := time.Unix(int64(pssmsg.Expire), 0)
if msgexp.Before(time.Now()) {
log.Trace("pss expired :/ ... dropping")
return nil
} else if msgexp.After(time.Now().Add(self.msgTTL)) {
return errors.New("Invalid TTL")
}
log.Trace("pss was for someone else :'( ... forwarding", "pss", common.ToHex(self.BaseAddr())) log.Trace("pss was for someone else :'( ... forwarding", "pss", common.ToHex(self.BaseAddr()))
return self.forward(pssmsg) self.outbox <- pssmsg
} }
log.Trace("pss for us, yay! ... let's process!", "pss", common.ToHex(self.BaseAddr())) log.Trace("pss for us, yay! ... let's process!", "pss", common.ToHex(self.BaseAddr()))
if !self.process(pssmsg) { if !self.process(pssmsg) {
err = self.forward(pssmsg) self.outbox <- pssmsg
} }
return err return err
} }
@ -335,10 +343,7 @@ func (self *Pss) process(pssmsg *PssMsg) bool {
if len(pssmsg.To) < addressLength { if len(pssmsg.To) < addressLength {
go func() { go func() {
err := self.forward(pssmsg) self.outbox <- pssmsg
if err != nil {
log.Warn("Redundant forward fail: %v", err)
}
}() }()
} }
handlers := self.getHandlers(psstopic) handlers := self.getHandlers(psstopic)
@ -401,7 +406,7 @@ func (self *Pss) generateSymmetricKey(topic Topic, address *PssAddress, addToCac
if err != nil { if err != nil {
return "", err return "", err
} }
self.addSymmetricKeyToPool(keyid, topic, address, addToCache) self.addSymmetricKeyToPool(keyid, topic, address, addToCache, false)
return keyid, nil return keyid, nil
} }
@ -418,20 +423,25 @@ func (self *Pss) generateSymmetricKey(topic Topic, address *PssAddress, addToCac
// Returns a string id that can be used to retrieve the key bytes // Returns a string id that can be used to retrieve the key bytes
// from the whisper backend (see pss.GetSymmetricKey()) // from the whisper backend (see pss.GetSymmetricKey())
func (self *Pss) SetSymmetricKey(key []byte, topic Topic, address *PssAddress, addtocache bool) (string, error) { func (self *Pss) SetSymmetricKey(key []byte, topic Topic, address *PssAddress, addtocache bool) (string, error) {
return self.setSymmetricKey(key, topic, address, addtocache, true)
}
func (self *Pss) setSymmetricKey(key []byte, topic Topic, address *PssAddress, addtocache bool, protected bool) (string, error) {
keyid, err := self.w.AddSymKeyDirect(key) keyid, err := self.w.AddSymKeyDirect(key)
if err != nil { if err != nil {
return "", err return "", err
} }
self.addSymmetricKeyToPool(keyid, topic, address, addtocache) self.addSymmetricKeyToPool(keyid, topic, address, addtocache, protected)
return keyid, nil return keyid, nil
} }
// adds a symmetric key to the pss key pool, and optionally adds the key // adds a symmetric key to the pss key pool, and optionally adds the key
// to the collection of keys used to attempt symmetric decryption of // to the collection of keys used to attempt symmetric decryption of
// incoming messages // incoming messages
func (self *Pss) addSymmetricKeyToPool(keyid string, topic Topic, address *PssAddress, addtocache bool) { func (self *Pss) addSymmetricKeyToPool(keyid string, topic Topic, address *PssAddress, addtocache bool, protected bool) {
psp := &pssPeer{ psp := &pssPeer{
address: address, address: address,
protected: protected,
} }
self.symKeyPoolMu.Lock() self.symKeyPoolMu.Lock()
if _, ok := self.symKeyPool[keyid]; !ok { if _, ok := self.symKeyPool[keyid]; !ok {
@ -644,29 +654,23 @@ func (self *Pss) send(to []byte, topic Topic, msg []byte, asymmetric bool, key [
Expire: uint32(time.Now().Add(self.msgTTL).Unix()), Expire: uint32(time.Now().Add(self.msgTTL).Unix()),
Payload: envelope, Payload: envelope,
} }
return self.forward(pssmsg) self.outbox <- pssmsg
return nil
} }
// Forwards a pss message to the peer(s) closest to the to recipient address in the PssMsg struct // Forwards a pss message to the peer(s) closest to the to recipient address in the PssMsg struct
// The recipient address can be of any length, and the byte slice will be matched to the MSB slice // The recipient address can be of any length, and the byte slice will be matched to the MSB slice
// of the peer address of the equivalent length. // of the peer address of the equivalent length.
func (self *Pss) forward(msg *PssMsg) error { func (self *Pss) forward(msg *PssMsg) {
to := make([]byte, addressLength) to := make([]byte, addressLength)
copy(to[:len(msg.To)], msg.To) copy(to[:len(msg.To)], msg.To)
// cache the message // message hash
digest, err := self.storeMsg(msg) digest, err := self.storeMsg(msg)
if err != nil { if err != nil {
log.Warn(fmt.Sprintf("could not store message %v to cache: %v", msg, err)) log.Warn(fmt.Sprintf("could not store message %v to cache: %v", msg, err))
} }
// flood guard:
// don't allow identical messages we saw shortly before
if self.checkFwdCache(nil, digest) {
log.Trace(fmt.Sprintf("pss relay block-cache match: FROM %x TO %x", self.Overlay.BaseAddr(), common.ToHex(msg.To)))
return nil
}
// send with kademlia // send with kademlia
// find the closest peer to the recipient and attempt to send // find the closest peer to the recipient and attempt to send
sent := 0 sent := 0
@ -696,7 +700,9 @@ func (self *Pss) forward(msg *PssMsg) error {
// get the protocol peer from the forwarding peer cache // get the protocol peer from the forwarding peer cache
sendMsg := fmt.Sprintf("MSG %x TO %x FROM %x VIA %x", digest, to, self.BaseAddr(), op.Address()) sendMsg := fmt.Sprintf("MSG %x TO %x FROM %x VIA %x", digest, to, self.BaseAddr(), op.Address())
self.fwdPoolMu.RLock()
pp := self.fwdPool[sp.Info().ID] pp := self.fwdPool[sp.Info().ID]
self.fwdPoolMu.RUnlock()
if self.checkFwdCache(op.Address(), digest) { if self.checkFwdCache(op.Address(), digest) {
log.Trace(fmt.Sprintf("%v: peer already forwarded to", sendMsg)) log.Trace(fmt.Sprintf("%v: peer already forwarded to", sendMsg))
return true return true
@ -730,11 +736,12 @@ func (self *Pss) forward(msg *PssMsg) error {
if sent == 0 { if sent == 0 {
log.Debug("unable to forward to any peers") log.Debug("unable to forward to any peers")
return nil time.Sleep(time.Millisecond)
self.outbox <- msg
} }
// cache the message
self.addFwdCache(digest) self.addFwdCache(digest)
return nil
} }
///////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////
@ -757,8 +764,8 @@ func (self *Pss) addFwdCache(digest pssDigest) error {
// check if message is in the cache // check if message is in the cache
func (self *Pss) checkFwdCache(addr []byte, digest pssDigest) bool { func (self *Pss) checkFwdCache(addr []byte, digest pssDigest) bool {
self.fwdCacheMu.Lock() self.fwdCacheMu.RLock()
defer self.fwdCacheMu.Unlock() defer self.fwdCacheMu.RUnlock()
entry, ok := self.fwdCache[digest] entry, ok := self.fwdCache[digest]
if ok { if ok {
if entry.expiresAt.After(time.Now()) { if entry.expiresAt.After(time.Now()) {
@ -785,3 +792,11 @@ func (self *Pss) storeMsg(msg *PssMsg) (pssDigest, error) {
copy(digest[:], key[:digestLength]) copy(digest[:], key[:digestLength])
return digest, nil return digest, nil
} }
func (self *Pss) isMsgExpired(msg *PssMsg) bool {
msgexp := time.Unix(int64(msg.Expire), 0)
if msgexp.Before(time.Now()) || msgexp.After(time.Now().Add(self.msgTTL)) {
return true
}
return false
}

View file

@ -477,7 +477,7 @@ func testSymSend(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
err = clients[1].Call(&rkeyids, "psstest_setSymKeys", rpubkeyhex, rrecvkey, lrecvkey, defaultSymKeySendLimit, topic, loaddrhex) err = clients[1].Call(&rkeyids, "psstest_setSymKeys", lpubkeyhex, rrecvkey, lrecvkey, defaultSymKeySendLimit, topic, loaddrhex)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -504,7 +504,7 @@ func testSymSend(t *testing.T) {
select { select {
case recvmsg := <-rmsgC: case recvmsg := <-rmsgC:
if !bytes.Equal(recvmsg.Msg, rmsg) { if !bytes.Equal(recvmsg.Msg, rmsg) {
t.Fatalf("node 2 received payload mismatch: expected %v, got %v", rmsg, recvmsg.Msg) t.Fatalf("node 2 received payload mismatch: expected %x, got %v", rmsg, recvmsg.Msg)
} }
case cerr := <-rctx.Done(): case cerr := <-rctx.Done():
t.Fatalf("test message timed out: %v", cerr) t.Fatalf("test message timed out: %v", cerr)