From b77b855f2c11f9e2c8e06abec342653aef3adc45 Mon Sep 17 00:00:00 2001 From: zelig Date: Fri, 5 May 2017 03:18:02 +0200 Subject: [PATCH 01/24] p2p/protocols: do handshake async w timeout --- p2p/protocols/protocol.go | 80 +++++++++++++++++++--------------- p2p/protocols/protocol_test.go | 8 ++-- 2 files changed, 48 insertions(+), 40 deletions(-) diff --git a/p2p/protocols/protocol.go b/p2p/protocols/protocol.go index 4318341e67..cf86d8dd36 100644 --- a/p2p/protocols/protocol.go +++ b/p2p/protocols/protocol.go @@ -32,6 +32,7 @@ package protocols import ( "fmt" "reflect" + "time" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/p2p" @@ -45,23 +46,21 @@ const ( ErrWrite ErrInvalidMsgCode ErrInvalidMsgType - ErrLocalHandshake - ErrRemoteHandshake + ErrHandshake ErrNoHandler ErrHandler ) // error description strings associated with the codes var errorToString = map[int]string{ - ErrMsgTooLong: "Message too long", - ErrDecode: "Invalid message (RLP error)", - ErrWrite: "Error sending message", - ErrInvalidMsgCode: "Invalid message code", - ErrInvalidMsgType: "Invalid message type", - ErrLocalHandshake: "Local handshake error", - ErrRemoteHandshake: "Remote handshake error", - ErrNoHandler: "No handler registered error", - ErrHandler: "Message handler error", + ErrMsgTooLong: "Message too long", + ErrDecode: "Invalid message (RLP error)", + ErrWrite: "Error sending message", + ErrInvalidMsgCode: "Invalid message code", + ErrInvalidMsgType: "Invalid message type", + ErrHandshake: "Handshake error", + ErrNoHandler: "No handler registered error", + ErrHandler: "Message handler error", } /* @@ -117,15 +116,16 @@ type CodeMap struct { Name string // name of the protocol Version uint // version MaxMsgSize int // max length of message payload size - codes []reflect.Type // index of codes to msg types - to create zero values + codepos int // the subsequent code + codes map[uint64]reflect.Type // index of codes to msg types - to create zero values messages map[reflect.Type]uint64 // index of types to codes, for sending by type } func (self *CodeMap) GetInterface(code uint64) (interface{}, bool) { - if int(code) > len(self.codes)-1 { + typ, found := self.codes[code] + if !found { return nil, false } - typ := self.codes[code] val := reflect.New(typ) return val.Interface(), true } @@ -135,23 +135,21 @@ func (self *CodeMap) GetCode(msg interface{}) (uint64, bool) { return code, found } -func NewCodeMap(name string, version uint, maxMsgSize int, msgs ...interface{}) *CodeMap { - self := &CodeMap{ +func NewCodeMap(name string, version uint, maxMsgSize int) *CodeMap { + return &CodeMap{ Name: name, Version: version, MaxMsgSize: maxMsgSize, messages: make(map[reflect.Type]uint64), } - self.Register(msgs...) - return self } func (self *CodeMap) Length() uint64 { - return uint64(len(self.codes)) + return uint64(self.codepos) } -func (self *CodeMap) Register(msgs ...interface{}) { - code := uint64(len(self.codes)) +func (self *CodeMap) Register(series int, msgs ...interface{}) { + code := series for _, msg := range msgs { typ := reflect.TypeOf(msg) _, found := self.messages[typ] @@ -160,9 +158,9 @@ func (self *CodeMap) Register(msgs ...interface{}) { continue } // next code assigned to message type typ - self.messages[typ] = code - self.codes = append(self.codes, typ) - code++ + self.messages[typ] = uint64(self.codepos) + self.codes[uint64(self.codepos)] = typ + self.codepos++ } } @@ -346,25 +344,35 @@ func (self *Peer) handleIncoming() (interface{}, error) { // * the argument is the local handshake to be sent to the remote peer // * expects a remote handshake back of the same type // returns the remote hs and an error -func (self *Peer) Handshake(hs interface{}) (interface{}, error) { +func (self *Peer) Handshake(hs interface{}, handshakeTimeout time.Duration) (rhs interface{}, err error) { typ := reflect.TypeOf(hs) _, found := self.ct.messages[typ] if !found { - return nil, errorf(ErrLocalHandshake, "unknown handshake message type: %v", typ) + return nil, errorf(ErrHandshake, "unknown handshake message type: %v", typ) } - errc := make(chan error) + errc := make(chan error, 1) go func() { - err := self.Send(hs) - if err != nil { - err = errorf(ErrLocalHandshake, "cannot send: %v", err) + if err := self.Send(hs); err != nil { + errc <- errorf(ErrHandshake, "cannot send: %v", err) } - errc <- err }() - // receiving and validating remote handshake, expect code - rhs, err := self.handleIncoming() - if err != nil { - return nil, errorf(ErrRemoteHandshake, "'%v': %v", self.ct.Name, err) + + hsc := make(chan interface{}) + go func() { + // receiving and validating remote handshake, expect code + rhs, err := self.handleIncoming() + if err != nil { + errc <- errorf(ErrHandshake, "'%v': %v", self.ct.Name, err) + return + } + hsc <- rhs + }() + + select { + case err = <-errc: + case rhs = <-hsc: + case <-time.NewTimer(handshakeTimeout).C: + err = errorf(ErrHandshake, "timeout") } - err = <-errc return rhs, err } diff --git a/p2p/protocols/protocol_test.go b/p2p/protocols/protocol_test.go index 3ca2fcb0d9..fc1238c5f7 100644 --- a/p2p/protocols/protocol_test.go +++ b/p2p/protocols/protocol_test.go @@ -56,7 +56,8 @@ const networkId = "420" // the run function here demonstrates a typical protocol using peerPool, handshake // and messages registered to handlers func newProtocol(pp *p2ptest.TestPeerPool) adapters.RunProtocol { - ct := NewCodeMap("test", 42, 1024, &protoHandshake{}, &hs0{}, &kill{}, &drop{}) + ct := NewCodeMap("test", 42, 1024) + ct.Register(0, &protoHandshake{}, &hs0{}, &kill{}, &drop{}) return func(p *p2p.Peer, rw p2p.MsgReadWriter) error { peer := NewPeer(p, ct, rw) @@ -70,13 +71,12 @@ func newProtocol(pp *p2ptest.TestPeerPool) adapters.RunProtocol { // for testing we can trigger self induced disconnect upon receiving drop message peer.Register(&drop{}, func(msg interface{}) error { - log.Trace("dropped") return fmt.Errorf("dropped") }) // initiate one-off protohandshake and check validity phs := &protoHandshake{ct.Version, networkId} - hs, err := peer.Handshake(phs) + hs, err := peer.Handshake(phs, time.Second) if err != nil { return err } @@ -88,7 +88,7 @@ func newProtocol(pp *p2ptest.TestPeerPool) adapters.RunProtocol { lhs := &hs0{42} // module handshake demonstrating a simple repeatable exchange of same-type message - hs, err = peer.Handshake(lhs) + hs, err = peer.Handshake(lhs, time.Second) if err != nil { return err } From c870e2be26f18896700c873a7fee4b652025a1f1 Mon Sep 17 00:00:00 2001 From: zelig Date: Thu, 11 May 2017 18:07:10 -0700 Subject: [PATCH 02/24] WIP network rewrite --- p2p/protocols/protocol.go | 44 ++- p2p/simulations/adapters/inproc.go | 3 +- p2p/simulations/network.go | 1 + pot/address.go | 59 +++ pot/pot.go | 7 +- swarm/network/discovery.go | 64 ++-- swarm/network/discovery_test.go | 3 +- swarm/network/hive.go | 250 +++++++----- swarm/network/hive_test.go | 29 +- swarm/network/kademlia.go | 546 +++++++++++++-------------- swarm/network/kademlia_test.go | 36 +- swarm/network/protocol.go | 159 ++++---- swarm/network/protocol_test.go | 30 +- swarm/network/pss.go | 37 +- swarm/network/pss_test.go | 90 +++-- swarm/network/simulations/overlay.go | 56 ++- swarm/network/test_overlay.go | 371 +++++++++--------- swarm/swarm.go | 74 ++-- 18 files changed, 1024 insertions(+), 835 deletions(-) diff --git a/p2p/protocols/protocol.go b/p2p/protocols/protocol.go index cf86d8dd36..12fce6b93d 100644 --- a/p2p/protocols/protocol.go +++ b/p2p/protocols/protocol.go @@ -135,21 +135,25 @@ func (self *CodeMap) GetCode(msg interface{}) (uint64, bool) { return code, found } +// NewCodeMap construct the code to type map for the protocol func NewCodeMap(name string, version uint, maxMsgSize int) *CodeMap { return &CodeMap{ Name: name, Version: version, MaxMsgSize: maxMsgSize, messages: make(map[reflect.Type]uint64), + codes: make(map[uint64]reflect.Type), } } +// Length returns the current highes codepos + 1 func (self *CodeMap) Length() uint64 { return uint64(self.codepos) } +// Register defines a new series of codes starting on series, incrementing func (self *CodeMap) Register(series int, msgs ...interface{}) { - code := series + self.codepos = series for _, msg := range msgs { typ := reflect.TypeOf(msg) _, found := self.messages[typ] @@ -196,7 +200,7 @@ type Peer struct { rw p2p.MsgReadWriter // p2p.MsgReadWriter to send messages to and read messages from handlers map[reflect.Type][]func(interface{}) error // message type -> message handler callback(s) map Errc chan error - wErrc chan error // write error channel + ready chan bool // blocking send until handshake finishes } // NewPeer returns a new peer @@ -204,12 +208,14 @@ type Peer struct { // the first two arguments are comming the arguments passed to p2p.Protocol.Run function // the third argument is the CodeMap describing the protocol messages and options func NewPeer(p *p2p.Peer, ct *CodeMap, rw p2p.MsgReadWriter) *Peer { + ready := make(chan bool) + defer close(ready) return &Peer{ ct: ct, Peer: p, rw: rw, Errc: make(chan error), - wErrc: make(chan error), + ready: ready, handlers: make(map[reflect.Type][]func(interface{}) error), } } @@ -269,11 +275,17 @@ func (self *Peer) Drop(err error) { // this low level call will be wrapped by libraries providing routed or broadcast sends // but often just used to forward and push messages to directly connected peers func (self *Peer) Send(msg interface{}) error { + <-self.ready + return self.send(msg) +} + +func (self *Peer) send(msg interface{}) error { code, found := self.ct.GetCode(msg) if !found { return errorf(ErrInvalidMsgType, "%v", code) } log.Trace(fmt.Sprintf("=> msg #%d TO %v : %v", code, self.ID(), msg)) + return p2p.Send(self.rw, uint64(code), msg) } @@ -350,29 +362,25 @@ func (self *Peer) Handshake(hs interface{}, handshakeTimeout time.Duration) (rhs if !found { return nil, errorf(ErrHandshake, "unknown handshake message type: %v", typ) } - errc := make(chan error, 1) - go func() { - if err := self.Send(hs); err != nil { - errc <- errorf(ErrHandshake, "cannot send: %v", err) - } - }() - - hsc := make(chan interface{}) + self.ready = make(chan bool) + received := make(chan bool) + defer close(self.ready) go func() { + defer close(received) // receiving and validating remote handshake, expect code - rhs, err := self.handleIncoming() + rhs, err = self.handleIncoming() if err != nil { - errc <- errorf(ErrHandshake, "'%v': %v", self.ct.Name, err) - return + err = errorf(ErrHandshake, "'%v': %v", self.ct.Name, err) } - hsc <- rhs }() + if e := self.send(hs); e != nil { + return nil, errorf(ErrHandshake, "cannot send: %v", e) + } select { - case err = <-errc: - case rhs = <-hsc: + case <-received: case <-time.NewTimer(handshakeTimeout).C: - err = errorf(ErrHandshake, "timeout") + err = errorf(ErrHandshake, "timeout after %v", handshakeTimeout) } return rhs, err } diff --git a/p2p/simulations/adapters/inproc.go b/p2p/simulations/adapters/inproc.go index 6f27607cf4..6a851fda29 100644 --- a/p2p/simulations/adapters/inproc.go +++ b/p2p/simulations/adapters/inproc.go @@ -211,7 +211,8 @@ func (self *SimNode) startRPC(service node.Service) error { self.lock.Lock() defer self.lock.Unlock() if self.client != nil { - return errors.New("RPC already started") + return nil + // return errors.New("RPC already started") } // add SimAdminAPI so that the network can call the diff --git a/p2p/simulations/network.go b/p2p/simulations/network.go index 0adc696ca8..4254ff4ba0 100644 --- a/p2p/simulations/network.go +++ b/p2p/simulations/network.go @@ -275,6 +275,7 @@ func (self *Network) startWithSnapshot(id *adapters.NodeId, snapshot []byte) err } log.Trace(fmt.Sprintf("starting node %v: %v using %v", id, node.Up, self.nodeAdapter.Name())) if err := node.Start(snapshot); err != nil { + log.Warn(fmt.Sprintf("start up failed: %v", err)) return err } node.Up = true diff --git a/pot/address.go b/pot/address.go index 4e4a76d03e..e638a11c7d 100644 --- a/pot/address.go +++ b/pot/address.go @@ -287,3 +287,62 @@ func (self *BoolAddress) PO(val PotVal, pos int) (po int, eq bool) { } return po, true } + +type BytesAddress interface { + Bytes() []byte +} + +type bytesAddress struct { + bytes []byte + toBytes func(v AnyVal) []byte +} + +func NewBytesVal(v AnyVal, f func(v AnyVal) []byte) *bytesAddress { + if f == nil { + f = ToBytes + } + b := f(v) + return &bytesAddress{b, f} +} + +func ToBytes(v AnyVal) []byte { + b, ok := v.([]byte) + if !ok { + ba, ok := v.(BytesAddress) + if !ok { + panic(fmt.Sprintf("unsupported value type %T", v)) + } + b = ba.Bytes() + } + return b +} + +func (a *bytesAddress) String() string { + return fmt.Sprintf("%08b", a.bytes) +} +func (a *bytesAddress) Bytes() []byte { + return a.bytes +} + +func (a *bytesAddress) PO(val PotVal, i int) (int, bool) { + return proximityOrder(a.bytes, a.toBytes(val), i) +} + +func proximityOrder(one, other []byte, pos int) (int, bool) { + for i := pos / 8; i < len(one); i++ { + if one[i] == other[i] { + continue + } + oxo := one[i] ^ other[i] + start := 0 + if i == pos/8 { + start = pos % 8 + } + for j := start; j < 8; j++ { + if (uint8(oxo)>>uint8(7-j))&0x01 != 0 { + return i*8 + j, false + } + } + } + return len(one) * 8, true +} diff --git a/pot/pot.go b/pot/pot.go index bb1d4c936e..eef5037b4e 100644 --- a/pot/pot.go +++ b/pot/pot.go @@ -45,6 +45,8 @@ type PotVal interface { String() string } +type AnyVal interface{} + // Pot constructor. Requires value of type PotVal to pin // and po to point to a span in the PotVal key // The pinned item counts towards the size @@ -245,11 +247,12 @@ func remove(t *pot, val PotVal) (r *pot, po int, found bool) { // if f returns v' <> v then v' is inserted into the Pot // if v' == v the pot is not changed // it panics if v'.PO(k, 0) says v and k are not equal -func (t *Pot) Swap(val PotVal, f func(v PotVal) PotVal) (po int, found bool, change bool) { +func (t *Pot) Swap(val AnyVal, f func(v PotVal) PotVal) (po int, found bool, change bool) { t.lock.Lock() defer t.lock.Unlock() + ba := NewBytesVal(val, nil) var t0 *pot - t0, po, found, change = swap(t.pot, val, f) + t0, po, found, change = swap(t.pot, ba, f) if change { t.pot = t0 } diff --git a/swarm/network/discovery.go b/swarm/network/discovery.go index 91ad122c6b..55a75ed858 100644 --- a/swarm/network/discovery.go +++ b/swarm/network/discovery.go @@ -16,7 +16,7 @@ var DiscoveryMsgs = []interface{}{ } type discPeer struct { - Peer + *bzzPeer overlay Overlay peers map[string]bool proxLimit uint8 // the proximity radius advertised by remote to subscribe to peers @@ -25,10 +25,10 @@ type discPeer struct { // discovery peer contructor // registers the handlers for discovery messages -func NewDiscovery(p Peer, o Overlay) *discPeer { +func NewDiscovery(p *bzzPeer, o Overlay) *discPeer { self := &discPeer{ overlay: o, - Peer: p, + bzzPeer: p, peers: make(map[string]bool), } self.seen(self) @@ -42,15 +42,15 @@ func NewDiscovery(p Peer, o Overlay) *discPeer { // NotifyPeer notifies the receiver remote end of a peer p or PO po. // callback for overlay driver -func (self *discPeer) NotifyPeer(p Peer, po uint8) error { +func (self *discPeer) NotifyPeer(p OverlayPeer, po uint8) error { log.Warn(fmt.Sprintf("peer %#v peers %v", p, self.peers)) if po < self.proxLimit || self.seen(p) { return nil } - log.Warn(fmt.Sprintf("notification about %x", p.OverlayAddr())) + log.Warn(fmt.Sprintf("notification about %x", p.Address())) resp := &peersMsg{ - Peers: []*peerAddr{&peerAddr{OAddr: p.OverlayAddr(), UAddr: p.UnderlayAddr()}}, // perhaps the PeerAddr interface is unnecessary generalization + Peers: []*bzzAddr{ToAddr(p)}, // perhaps the PeerAddr interface is unnecessary generalization } return self.Send(resp) } @@ -83,7 +83,7 @@ disconnected // used for communicating about known peers // relevant for bootstrapping connectivity and updating peersets type peersMsg struct { - Peers []*peerAddr + Peers []*bzzAddr } func (self peersMsg) String() string { @@ -113,14 +113,15 @@ func (self *discPeer) handleSubPeersMsg(msg interface{}) error { spm := msg.(*subPeersMsg) self.proxLimit = spm.ProxLimit if !self.sentPeers { - var peers []*peerAddr - self.overlay.EachLivePeer(self.OverlayAddr(), 255, func(p Peer, po int, isproxbin bool) bool { + var peers []*bzzAddr + self.overlay.EachConn(self.Over(), 255, func(p OverlayConn, po int, isproxbin bool) bool { if uint8(po) < self.proxLimit { return false } log.Warn(fmt.Sprintf("peer %#v proxlimit %v", p, self.proxLimit)) - self.seen(p.(*discPeer).Peer) - peers = append(peers, &peerAddr{p.OverlayAddr(), p.UnderlayAddr()}) + if !self.seen(p) { + peers = append(peers, ToAddr(p)) + } return true }) log.Warn(fmt.Sprintf("found initial %v peers not farther than %v", len(peers), self.proxLimit)) @@ -139,35 +140,37 @@ func (self *discPeer) handleSubPeersMsg(msg interface{}) error { // Register interface method func (self *discPeer) handlePeersMsg(msg interface{}) error { // register all addresses - var nas []PeerAddr - for _, na := range msg.(*peersMsg).Peers { - addr := PeerAddr(na) - nas = append(nas, addr) - self.seen(addr) - } - - if len(nas) == 0 { + as := msg.(*peersMsg).Peers + if len(as) == 0 { log.Debug(fmt.Sprintf("whoops, no peers in incoming peersMsg from %v", self)) return nil } - log.Debug(fmt.Sprintf("got peer addresses from %x, %v (%v)", self.OverlayAddr(), nas, len(nas))) - return self.overlay.Register(nas...) + + var c chan OverlayAddr + go func() { + for _, a := range as { + self.seen(a) + c <- a + } + }() + return self.overlay.Register(c) } // handleGetPeersMsg is called by the protocol when receiving a // peerset (for target address) request // peers suggestions are retrieved from the overlay topology driver -// using the EachLivePeer interface iterator method +// using the EachConn interface iterator method // peers sent are remembered throughout a session and not sent twice func (self *discPeer) handleGetPeersMsg(msg interface{}) error { - var peers []*peerAddr + var peers []*bzzAddr req := msg.(*getPeersMsg) i := 0 - self.overlay.EachLivePeer(self.OverlayAddr(), int(req.Order), func(n Peer, po int, isproxbin bool) bool { + self.overlay.EachConn(self.Over(), int(req.Order), func(p OverlayConn, po int, isproxbin bool) bool { i++ // only send peers we have not sent before in this session - if self.seen(n) { - peers = append(peers, &peerAddr{n.OverlayAddr(), n.UnderlayAddr()}) + a := ToAddr(p) + if self.seen(a) { + peers = append(peers, a) } return len(peers) < int(req.Max) }) @@ -189,9 +192,8 @@ func RequestOrder(k Overlay, order, broadcastSize, maxPeers uint8) { } var i uint8 //var err error - k.EachLivePeer(nil, 255, func(n Peer, po int, isproxbin bool) bool { - log.Trace(fmt.Sprintf("%T sent to %v", req, n)) - if err := n.Send(req); err == nil { + k.EachConn(nil, 255, func(p OverlayConn, po int, isproxbin bool) bool { + if err := p.(Conn).Send(req); err == nil { i++ if i >= broadcastSize { return false @@ -202,8 +204,8 @@ func RequestOrder(k Overlay, order, broadcastSize, maxPeers uint8) { log.Info(fmt.Sprintf("requesting bees of PO%03d from %v/%v (each max %v)", order, i, broadcastSize, maxPeers)) } -func (self *discPeer) seen(p PeerAddr) bool { - k := NodeId(p).NodeID.String() +func (self *discPeer) seen(p OverlayPeer) bool { + k := string(p.Address()) if self.peers[k] { return true } diff --git a/swarm/network/discovery_test.go b/swarm/network/discovery_test.go index d737b3e04f..22a61a04ea 100644 --- a/swarm/network/discovery_test.go +++ b/swarm/network/discovery_test.go @@ -18,7 +18,7 @@ func TestDiscovery(t *testing.T) { to := NewKademlia(addr.OAddr, NewKadParams()) ct := BzzCodeMap(DiscoveryMsgs...) - services := func(p Peer) error { + services := func(p *bzzPeer) error { dp := NewDiscovery(p, to) to.On(dp) log.Trace(fmt.Sprintf("kademlia on %v", p)) @@ -32,7 +32,6 @@ func TestDiscovery(t *testing.T) { defer s.Stop() s.runHandshakes() - // o := 0 s.TestExchanges(p2ptest.Exchange{ Label: "outgoing SubPeersMsg", Expects: []p2ptest.Expect{ diff --git a/swarm/network/hive.go b/swarm/network/hive.go index 61642a1532..2ee8cd3b46 100644 --- a/swarm/network/hive.go +++ b/swarm/network/hive.go @@ -17,6 +17,7 @@ package network import ( + "encoding/json" "fmt" "sync" "time" @@ -39,38 +40,51 @@ and relay the peer request process to the Overlay module peer connections and disconnections are reported and registered to keep the nodetable uptodate */ + +// Overlay is the interface to Jaak ahd ka)a type Overlay interface { - Register(...PeerAddr) error + Register(chan OverlayAddr) error - On(Peer) - Off(Peer) + On(OverlayPeer) + Off(OverlayConn) - EachLivePeer([]byte, int, func(Peer, int, bool) bool) - EachPeer([]byte, int, func(PeerAddr, int) bool) + EachConn([]byte, int, func(OverlayConn, int, bool) bool) + EachAddr([]byte, int, func(OverlayAddr, int) bool) - SuggestPeer() (PeerAddr, int, bool) + SuggestPeer() (OverlayAddr, int, bool) String() string - GetAddr() PeerAddr + BaseAddr() []byte +} + +// ReadWriter interface to persist known peers, uses disk for real nodes +type ReadWriter interface { + ReadAll(string) ([]byte, error) + WriteAll(string, []byte) error } // Hive implements the PeerPool interface type Hive struct { - *HiveParams // settings - Overlay // the overlay topology driver - lock sync.Mutex - quit chan bool - toggle chan bool - more chan bool + *HiveParams // settings + Overlay // the overlay topology driver + RW ReadWriter // ReadWriter + + // bookkeeping + lock sync.Mutex + quit chan bool + toggle chan bool + more chan bool } +// HiveParams holds the config options to hive type HiveParams struct { - Discovery bool - PeersBroadcastSetSize uint8 - MaxPeersPerRequest uint8 - CallInterval uint + Discovery bool // if want discovery of not + PeersBroadcastSetSize uint8 // how many peers to use when relaying + MaxPeersPerRequest uint8 // max size for peer address batches + CallInterval uint // polling interval fir=== } +// NewHiveParams returns hive config with only the func NewHiveParams() *HiveParams { return &HiveParams{ Discovery: true, @@ -81,8 +95,8 @@ func NewHiveParams() *HiveParams { } // Hive constructor embeds both arguments -// HiveParams config parameters -// Overlay Topology Driver Interface +// HiveParams: config parameters +// Overlay: Topology Driver Interface func NewHive(params *HiveParams, overlay Overlay) *Hive { return &Hive{ HiveParams: params, @@ -91,11 +105,16 @@ func NewHive(params *HiveParams, overlay Overlay) *Hive { } // Start receives network info only at startup -// connectPeer is a function to connect to a peer based on its NodeID or enode URL +// server is used to connect to a peer based on its NodeID or enode URL // these are called on the p2p.Server which runs on the node // af() returns an arbitrary ticker channel -func (self *Hive) Start(server p2p.Server, af func() <-chan time.Time) error { - +// rw is a read writer for json configs +func (self *Hive) Start(server p2p.Server, af func() <-chan time.Time, rw ReadWriter) error { + if rw != nil { + if err := self.loadPeers(); err != nil { + return err + } + } self.toggle = make(chan bool) self.more = make(chan bool, 1) self.quit = make(chan bool) @@ -115,9 +134,9 @@ func (self *Hive) Start(server p2p.Server, af func() <-chan time.Time) error { if addr != nil { log.Info(fmt.Sprintf("========> connect to bee %v", addr)) - node, err := discover.ParseNode(NodeId(addr).NodeID.String()) + under, err := discover.ParseNode(string(addr.(Addr).Under())) if err == nil { - server.AddPeer(node) + server.AddPeer(under) } else { log.Error(fmt.Sprintf("===X====> connect to bee %v failed: invalid node URL: %v", addr, err)) } @@ -127,7 +146,7 @@ func (self *Hive) Start(server p2p.Server, af func() <-chan time.Time) error { want = want && self.Discovery if want { - go RequestOrder(self.Overlay, uint8(order), self.PeersBroadcastSetSize, self.MaxPeersPerRequest) + RequestOrder(self.Overlay, uint8(order), self.PeersBroadcastSetSize, self.MaxPeersPerRequest) } select { @@ -136,20 +155,98 @@ func (self *Hive) Start(server p2p.Server, af func() <-chan time.Time) error { case <-self.quit: return } - log.Info(fmt.Sprintf("%v", self)) + // log.Info(fmt.Sprintf("%v", self)) } }() return nil } +// Stop terminates the updateloop and saves the peers +func (self *Hive) Stop() { + if self.RW != nil { + self.savePeers() + } + // closing toggle channel quits the updateloop + close(self.quit) +} + +// default ticker, tickinterval is taken from KadParams.CallInterval func (self *Hive) ticker() <-chan time.Time { return time.NewTicker(time.Duration(self.CallInterval) * time.Millisecond).C } +// Add is called at the end of a successful protocol handshake +// to register a connected (live) peer +func (self *Hive) Add(p *bzzPeer) error { + defer self.wake() + dp := NewDiscovery(p, self.Overlay) + log.Debug(fmt.Sprintf("to add new bee %v", p)) + self.On(dp) + self.String() + log.Debug(fmt.Sprintf("%v", self)) + return nil +} + +// Remove called after peer is disconnected +func (self *Hive) Remove(p *bzzPeer) { + defer self.wake() + log.Debug(fmt.Sprintf("remove bee %v", p)) + self.Off(p) +} + +// NodeInfo function is used by the p2p.server RPC interface to display +// protocol specific node information +func (self *Hive) NodeInfo() interface{} { + return interface{}(self.String()) +} + +// PeerInfo function is used by the p2p.server RPC interface to display +// protocol specific information any connected peer referred to by their NodeID +func (self *Hive) PeerInfo(id discover.NodeID) interface{} { + self.lock.Lock() + defer self.lock.Unlock() + addr := NewAddrFromNodeId(adapters.NewNodeId(id[:])) + return interface{}(addr) +} + +// Healthy reports the health state of the kademlia connectivity +// +func (self *Hive) Healthy() bool { + // TODO: determine if we have enough peers to consider the network + // to be healthy + return true +} + +// wake triggers +func (self *Hive) wake() { + select { + case self.more <- true: + log.Trace("hive woken up") + case <-self.quit: + default: + log.Trace("hive already awake") + } +} + +// HexToBytes reads a hex string ontp +func HexToBytes(s string) []byte { + id := discover.MustHexID(s) + return id[:] +} + +// ToAddr returns the serialisable version of u +func ToAddr(pa OverlayPeer) *bzzAddr { + if addr, ok := pa.(*bzzAddr); ok { + return addr + } + return pa.(*bzzPeer).bzzAddr +} + // keepAlive is a forever loop // in its awake state it periodically triggers connection attempts // by writing to self.more until Kademlia Table is saturated // wake state is toggled by writing to self.toggle +// it goes to sleep mode if table is saturated // it restarts if the table becomes non-full again due to disconnections func (self *Hive) keepAlive(af func() <-chan time.Time) { log.Trace("keep alive loop started") @@ -173,63 +270,48 @@ func (self *Hive) keepAlive(af func() <-chan time.Time) { } } -// Add is called at the end of a successful protocol handshake -// to register a connected (live) peer -func (self *Hive) Add(p Peer) error { - defer self.wake() - dp := NewDiscovery(p, self.Overlay) - log.Debug(fmt.Sprintf("to add new bee %v", p)) - self.On(dp) - self.String() - log.Debug(fmt.Sprintf("%v", self)) +// loadPeers, savePeer implement persistence callback/ +func (self *Hive) loadPeers() error { + rw := self.RW + data, err := rw.ReadAll("peers") + if err != nil { + return err + } + if data == nil { + return nil + } + var as []*bzzAddr + if err := json.Unmarshal(data, &as); err != nil { + return err + } + + var c chan OverlayAddr + defer close(c) + go func() { + for _, a := range as { + c <- a + } + }() + return self.Overlay.Register(c) +} + +// savePeers, savePeer implement persistence callback/ +func (self *Hive) savePeers() error { + var peers []*bzzAddr + self.Overlay.EachAddr(nil, 256, func(pa OverlayAddr, i int) bool { + if pa == nil { + log.Warn(fmt.Sprintf("empty addr: %v", i)) + return true + } + peers = append(peers, ToAddr(pa)) + return true + }) + data, err := json.Marshal(peers) + if err != nil { + return fmt.Errorf("could not encode peers: %v", err) + } + if err := self.RW.WriteAll("peers", data); err != nil { + return fmt.Errorf("could not save peers: %v", err) + } return nil } - -// Remove called after peer is disconnected -func (self *Hive) Remove(p Peer) { - defer self.wake() - log.Debug(fmt.Sprintf("remove bee %v", p)) - self.Off(p) -} - -// NodeInfo function is used by the p2p.server RPC interface to display -// protocol specific node information -func (self *Hive) NodeInfo() interface{} { - return interface{}(self.String()) -} - -// PeerInfo function is used by the p2p.server RPC interface to display -// protocol specific information any connected peer referred to by their NodeID -func (self *Hive) PeerInfo(id discover.NodeID) interface{} { - self.lock.Lock() - defer self.lock.Unlock() - addr := NewPeerAddrFromNodeId(adapters.NewNodeId(id[:])) - return interface{}(addr) -} - -// Stop terminates the updateloop -func (self *Hive) Stop() { - // closing toggle channel quits the updateloop - close(self.quit) -} - -func (self *Hive) Healthy() bool { - // TODO: determine if we have enough peers to consider the network - // to be healthy - return true -} - -func (self *Hive) wake() { - select { - case self.more <- true: - log.Trace("hive woken up") - case <-self.quit: - default: - log.Trace("hive already awake") - } -} - -func HexToBytes(s string) []byte { - id := discover.MustHexID(s) - return id[:] -} diff --git a/swarm/network/hive_test.go b/swarm/network/hive_test.go index 54a8c9e3bf..aaa90e71a3 100644 --- a/swarm/network/hive_test.go +++ b/swarm/network/hive_test.go @@ -30,12 +30,13 @@ func (self *testConnect) connect(na string) error { func newHiveTester(t *testing.T, params *HiveParams) (*bzzTester, *Hive) { // setup - addr := RandomAddr() // tested peers peer address - to := NewTestOverlay(addr.OverlayAddr()) // overlay topology drive - pp := NewHive(params, to) // hive + addr := RandomAddr() // tested peers peer address + // to := NewTestOverlay(addr.Over()) // overlay topology drive + pp := NewHive(params, nil) // hive + // pp := NewHive(params, to) // hive ct := BzzCodeMap(DiscoveryMsgs...) // bzz protocol code map - services := func(p Peer) error { + services := func(p *bzzPeer) error { pp.Add(p) p.DisconnectHook(func(err error) { pp.Remove(p) @@ -53,14 +54,14 @@ func TestOverlayRegistration(t *testing.T) { defer s.Stop() id := s.Ids[0] - raddr := NewPeerAddrFromNodeId(id) + raddr := NewAddrFromNodeId(id) s.runHandshakes() // hive should have called the overlay - if pp.Overlay.(*testOverlay).posMap[string(raddr.OverlayAddr())] == nil { - t.Fatalf("Overlay#On not called on new peer") - } + // if pp.Overlay.(*testOverlay).posMap[string(raddr.Over())] == nil { + // t.Fatalf("Overlay#On not called on new peer") + // } } @@ -70,7 +71,7 @@ func TestRegisterAndConnect(t *testing.T) { defer s.Stop() id := s.Ids[0] - raddr := NewPeerAddrFromNodeId(id) + raddr := NewAddrFromNodeId(id) pp.Register(raddr) @@ -82,18 +83,18 @@ func TestRegisterAndConnect(t *testing.T) { }, ticker: make(chan time.Time), } - pp.Start(s, tc.ping) + pp.Start(s, tc.ping, nil) defer pp.Stop() tc.ticker <- time.Now() s.runHandshakes() - if pp.Overlay.(*testOverlay).posMap[string(raddr.OverlayAddr())] == nil { - t.Fatalf("Overlay#On not called on new peer") - } + // if pp.Overlay.(*testOverlay).posMap[string(raddr.Over())] == nil { + // t.Fatalf("Overlay#On not called on new peer") + // } // retrieve and broadcast - ord := order(raddr.OverlayAddr()) + ord := order(raddr.Over()) o := 0 if ord == 0 { o = 1 diff --git a/swarm/network/kademlia.go b/swarm/network/kademlia.go index 789575e804..72dc7c9ebf 100644 --- a/swarm/network/kademlia.go +++ b/swarm/network/kademlia.go @@ -46,24 +46,20 @@ a guaranteed constant maximum limit on the number of hops needed to reach one node from the other. */ -type KadDiscovery interface { - NotifyPeer(Peer, uint8) error - NotifyProx(uint8) error -} - +// KadParams holds the config params for Kademlia type KadParams struct { // adjustable parameters - MaxProxDisplay int - MinProxBinSize int - MinBinSize int - MaxBinSize int - RetryInterval int - RetryExponent int - MaxRetries int - PruneInterval int + MaxProxDisplay int // number of rows the table shows + MinProxBinSize int // nearest neighbour core minimum cardinality + MinBinSize int // minimum number of peers in a row + MaxBinSize int // maximum number of peers in a row before pruning + RetryInterval int // initial interval before a peer is first redialed + RetryExponent int // exponent to multiply retry intervals with + MaxRetries int // maximum number of redial attempts + PruneInterval int // interval between peer pruning cycles } -// NewKadParams() returns a params struct with default values +// NewKadParams returns a params struct with default values func NewKadParams() *KadParams { return &KadParams{ MaxProxDisplay: 8, @@ -79,141 +75,94 @@ func NewKadParams() *KadParams { // Kademlia is a table of live peers and a db of known peers type Kademlia struct { - addr PeerAddr // immutable baseaddress of the table - // addr *pot.HashAddress // immutable baseaddress of the table - *KadParams // Kademlia configuration parameters - addrs, peers *pot.Pot // pots container for peers - lastProxLimit uint8 // stores the last calculated proxlimit + *KadParams // Kademlia configuration parameters + base []byte // immutable baseaddress of the table + addrs *pot.Pot // pots container for known peer addresses + conns *pot.Pot // pots container for live peer connections + depth uint8 // stores the last calculated depth } -// NewKademlia(addr, params) creates a Kademlia table for base address addr +// NewKademlia creates a Kademlia table for base address addr // with parameters as in params // if params is nil, it uses default values func NewKademlia(addr []byte, params *KadParams) *Kademlia { if params == nil { params = NewKadParams() } - self := &Kademlia{ - addr: &peerAddr{OAddr: addr}, + return &Kademlia{ + base: addr, KadParams: params, addrs: pot.NewPot(nil, 0), - peers: pot.NewPot(nil, 0), + conns: pot.NewPot(nil, 0), } - return self } -// Prune implements a forever loop reacting to a ticker time channel given -// as the first argument -// the loop quits if the channel is closed -// it checks each kademlia bin and if the peer count is higher than -// the MaxBinSize parameter it drops the oldest n peers such that -// the bin is reduced to MinBinSize peers thus leaving slots to newly -// connecting peers -func (self *Kademlia) Prune(c <-chan time.Time) { - go func() { - for _ = range c { - log.Debug("pruning...") - total := 0 - self.peers.EachBin(self.addr, 0, func(po, size int, f func(func(pot.PotVal, int) bool) bool) bool { - extra := size - self.MinBinSize - if size > self.MaxBinSize { - n := 0 - f(func(v pot.PotVal, po int) bool { - p := v.(*KadPeer).Peer - if p != nil { - p.Drop(fmt.Errorf("bucket full")) - } - n++ - return n < extra - }) - total += extra - } - return true - }) - log.Debug(fmt.Sprintf("pruned %v peers", total)) - } - }() +type Notifier interface { + NotifyPeer(OverlayConn, uint8) error + NotifyDepth(uint8) error } -// KadPeer represents a Kademlia Peer and extends -// * PeerAddr interface (overlay and underlay addresses) -// * Peer interface (id, last seen, drop) -type KadPeer struct { - PeerAddr - Peer Peer +// OverlayPeer interface captures the common aspect of view of a peer from the Overlay +// topology driver +type OverlayPeer interface { + Address() []byte +} + +// OverlayConn represents a connected peer +type OverlayConn interface { + OverlayPeer + Drop(error) // call to indicate a peer should be expunged + Off() OverlayAddr // call to return a persitent OverlayAddr +} + +type OverlayAddr interface { + OverlayPeer + On(OverlayConn) OverlayConn // call to return the connected peer + Update(OverlayAddr) OverlayAddr // returns the updated version of the original +} + +// entry represents a Kademlia table entry (an extension of OverlayPeer) +// implements the pot.PotVal interface via BytesAddress, so entry can be +// used directly as a pot element +type entry struct { + pot.PotVal + OverlayPeer seenAt time.Time retries int } -func (self *KadPeer) String() string { - if self == nil { - return "" - } - return fmt.Sprintf("%x", self.OverlayAddr()) -} - -func (self *Kademlia) callable(val pot.PotVal) *KadPeer { - kp := val.(*KadPeer) - // not callable if peer is live or exceeded maxRetries - if kp.Peer != nil || kp.retries > self.MaxRetries { - log.Trace(fmt.Sprintf("peer %v (%T) not callable", kp, kp.Peer)) - return nil - } - // calculate the allowed number of retries based on time lapsed since last seen - timeAgo := time.Since(kp.seenAt) - var retries int - for delta := int(timeAgo) / self.RetryInterval; delta > 0; delta /= self.RetryExponent { - log.Trace(fmt.Sprintf("delta: %v", delta)) - retries++ - } - - // this is never called concurrently, so safe to increment - // peer can be retried again - if retries < kp.retries { - log.Trace(fmt.Sprintf("log time needed before retry %v, wait only warrants %v", kp.retries, retries)) - return nil - } - kp.retries++ - log.Trace(fmt.Sprintf("peer %v is callable", kp)) - - return kp -} - -// NewKadPeer creates a kademlia peer from a PeerAddr interface -func NewKadPeer(na PeerAddr) *KadPeer { - return &KadPeer{ - PeerAddr: na, - seenAt: time.Now(), +// newEntry creates a kademlia peer from an OverlayPeer interface +func newEntry(p OverlayPeer) *entry { + return &entry{ + PotVal: pot.NewBytesVal(p, nil), + OverlayPeer: p, + seenAt: time.Now(), } } -// retrieve the base address -// which is the overlayaddress used by peers to reach us -func (self *Kademlia) GetAddr() PeerAddr { - return self.addr +func (self *entry) String() string { + return fmt.Sprintf("%x", self.Address()) } -// Register(nas) enters each PeerAddr as kademlia peers into the -// database of known peers -func (self *Kademlia) Register(nas ...PeerAddr) error { - label := fmt.Sprintf("%x", RandomAddr().OverlayAddr()) +// Register enters each OverlayAddr as kademlia peer record into the +// database of known peer addresses +func (self *Kademlia) Register(peers chan OverlayAddr) error { + if len(peers) == 0 { + return fmt.Errorf("empty peers list") + } np := pot.NewPot(nil, 0) - for _, na := range nas { - if bytes.Equal(na.OverlayAddr(), self.addr.OverlayAddr()) { - log.Warn(fmt.Sprintf("[%06s] add peers: %x is self.. skipped ", label, self.addr.OverlayAddr())) - continue + defer func() { self.addrs.Merge(np) }() + for p := range peers { + // error if self received, peer should know better + if bytes.Equal(p.Address(), self.base) { + return fmt.Errorf("add peers: %x is self", self.base) } - p := NewKadPeer(na) - np, _, _ = pot.Add(np, pot.PotVal(p)) + np, _, _ = pot.Add(np, pot.PotVal(newEntry(p))) } - oldpeers := pot.NewPot(nil, 0) - oldpeers.Merge(self.addrs) - self.addrs.Merge(np) + // TODO: remove this check m := make(map[string]bool) self.addrs.Each(func(val pot.PotVal, i int) bool { _, found := m[val.String()] - // TODO: remove this check - // log.Debug(fmt.Sprintf("-> %v %v", val, i)) if found { panic("duplicate found") } @@ -223,148 +172,32 @@ func (self *Kademlia) Register(nas ...PeerAddr) error { return nil } -// On(p) inserts the peer as a kademlia peer into the live peers -func (self *Kademlia) On(p Peer) { - kp := NewKadPeer(p) - kp.Peer = p - self.peers.Swap(kp, func(v pot.PotVal) pot.PotVal { - // if not found live - if v == nil { - // switch the offline peer - self.addrs.Swap(kp, func(v pot.PotVal) pot.PotVal { - return pot.PotVal(kp) - }) - // insert new peer - return pot.PotVal(kp) - } - // found among live peers, do nothing - return v - }) - prox := self.proxLimit() - - vp, ok := kp.Peer.(KadDiscovery) - if !ok { - // log.Trace(fmt.Sprintf("not discovery peer %T", kp)) - return - } - go vp.NotifyProx(uint8(prox)) - f := func(val pot.PotVal, po int) { - dp := val.(*KadPeer).Peer.(KadDiscovery) - log.Debug(fmt.Sprintf("peer %v notified of %v (%v)", dp, kp, po)) - dp.NotifyPeer(kp.Peer, uint8(po)) - if uint8(prox) != self.lastProxLimit { - self.lastProxLimit = uint8(prox) - dp.NotifyProx(uint8(prox)) - } - log.Debug("peer notified") - } - self.peers.EachNeighbourAsync(kp, 1024, 255, f, false) -} - -// Off removes a peer from among live peers -func (self *Kademlia) Off(p Peer) { - kp := NewKadPeer(p) - self.addrs.Swap(kp, func(v pot.PotVal) pot.PotVal { - if v != nil { - self.peers.Swap(kp, func(v pot.PotVal) pot.PotVal { - return nil - }) - } - return nil - }) -} - -type ByteAddr struct { - key []byte -} - -// EachLivePeer(base, po, f) is an iterator applying f to each live peer -// that has proximity order po or less as measured from the base -// if base is nil, kademlia base address is used -func (self *Kademlia) EachLivePeer(base []byte, o int, f func(Peer, int, bool) bool) { - var p pot.PotVal - if base == nil { - p = pot.PotVal(self.addr) - } else { - p = pot.PotVal(&peerAddr{OAddr: base}) - } - self.peers.EachNeighbour(p, func(val pot.PotVal, po int) bool { - if po > o { - return true - } - isproxbin := false - if l, _ := p.PO(val, 0); l >= self.proxLimit() { - isproxbin = true - } - return f(val.(*KadPeer).Peer, po, isproxbin) - }) -} - -// EachPeer(base, po, f) is an iterator applying f to each known peer -// that has proximity order po or less as measured from the base -// if base is nil, kademlia base address is used -func (self *Kademlia) EachPeer(base []byte, o int, f func(PeerAddr, int) bool) { - var p pot.PotVal - if base == nil { - p = pot.PotVal(self.addr) - } else { - p = pot.NewHashAddressFromBytes(base) - } - self.addrs.EachNeighbour(p, func(val pot.PotVal, po int) bool { - if po > o { - return true - } - return f(val.(*KadPeer).Peer, po) - }) -} - -// proxLimit() returns the proximity order that defines the distance of -// the nearest neighbour set with cardinality >= MinProxBinSize -// if there is altogether less than MinProxBinSize peers it returns 0 -func (self *Kademlia) proxLimit() int { - if self.peers.Size() < self.MinProxBinSize { - return 0 - } - var proxLimit int - var size int - f := func(v pot.PotVal, i int) bool { - size++ - proxLimit = i - return size < self.MinProxBinSize - } - self.peers.EachNeighbour(pot.PotVal(self.addr), f) - return proxLimit -} - // SuggestPeer returns a known peer for the lowest proximity bin for the -// lowest bincount below proxLimit +// lowest bincount below depth // naturally if there is an empty row it returns a peer for that // -func (self *Kademlia) SuggestPeer() (p PeerAddr, o int, want bool) { +func (self *Kademlia) SuggestPeer() (a OverlayAddr, o int, want bool) { minsize := self.MinBinSize - proxLimit := self.proxLimit() + depth := self.Depth() // if there is a callable neighbour within the current proxBin, connect // this makes sure nearest neighbour set is fully connected - log.Trace(fmt.Sprintf("candidate prox peer checking above PO %v", proxLimit)) + log.Trace(fmt.Sprintf("candidate prox peer checking above PO %v", depth)) var ppo int - self.addrs.EachNeighbour(self.addr, func(val pot.PotVal, po int) bool { - r := self.callable(val) - if r == nil { - return po >= proxLimit - } - p = r + ba := pot.NewBytesVal(self.base, nil) + self.addrs.EachNeighbour(ba, func(val pot.PotVal, po int) bool { + a = self.callable(val) ppo = po - return false + return a != nil && po >= depth }) - if p != nil { - log.Trace(fmt.Sprintf("candidate prox peer found: %v (%v), %v", p, ppo, p)) - return p, 0, false + if a != nil { + log.Trace(fmt.Sprintf("candidate prox peer found: %v (%v)", a, ppo)) + return a, 0, false } - log.Trace(fmt.Sprintf("no candidate prox peers to connect to (ProxLimit: %v, minProxSize: %v)", proxLimit, self.MinProxBinSize)) + log.Trace(fmt.Sprintf("no candidate prox peers to connect to (Depth: %v, minProxSize: %v)", depth, self.MinProxBinSize)) var bpo []int prev := -1 - self.peers.EachBin(self.addr, 0, func(po, size int, f func(func(val pot.PotVal, i int) bool) bool) bool { + self.conns.EachBin(pot.NewBytesVal(self.base, nil), 0, func(po, size int, f func(func(val pot.PotVal, i int) bool) bool) bool { log.Trace(fmt.Sprintf("check PO%02d: ", po)) prev++ if po > prev { @@ -375,7 +208,7 @@ func (self *Kademlia) SuggestPeer() (p PeerAddr, o int, want bool) { minsize = size bpo = append(bpo, po) } - return size > 0 && po < proxLimit + return size > 0 && po < depth }) // all buckets are full // minsize == self.MinBinSize @@ -387,28 +220,165 @@ func (self *Kademlia) SuggestPeer() (p PeerAddr, o int, want bool) { // try to select a candidate peer for i := len(bpo) - 1; i >= 0; i-- { // find the first callable peer - self.addrs.EachBin(self.addr, bpo[i], func(po, size int, f func(func(val pot.PotVal, i int) bool) bool) bool { + self.addrs.EachBin(ba, bpo[i], func(po, size int, f func(func(pot.PotVal, int) bool) bool) bool { // for each bin we find callable candidate peers - f(func(val pot.PotVal, i int) bool { - r := self.callable(val) - log.Trace(fmt.Sprintf("check PO%02d: ", po)) - if r == nil { - return i < proxLimit - } - p = r - return false + log.Trace(fmt.Sprintf("check PO%02d: ", po)) + f(func(val pot.PotVal, j int) bool { + a = self.callable(val) + return a != nil && po < depth }) return false }) // found a candidate - if p != nil { + if a != nil { break } // cannot find a candidate, ask for more for this proximity bin specifically o = bpo[i] want = true } - return p, o, want + return a, o, want +} + +// On inserts the peer as a kademlia peer into the live peers +func (self *Kademlia) On(p OverlayPeer) { + e := newEntry(p) + self.conns.Swap(p, func(v pot.PotVal) pot.PotVal { + // if not found live + if v == nil { + // insert new online peer into addrs + self.addrs.Swap(p, func(v pot.PotVal) pot.PotVal { + return e + }) + // insert new online peer into conns + return e + } + // found among live peers, do nothing + return v + }) + + np, ok := p.(Notifier) + if !ok { + return + } + + depth := uint8(self.Depth()) + if depth != self.depth { + self.depth = depth + } else { + depth = 0 + } + + go np.NotifyDepth(depth) + f := func(val pot.PotVal, po int) { + dp := val.(Notifier) + dp.NotifyPeer(p.(OverlayConn), uint8(po)) + log.Trace(fmt.Sprintf("peer %v notified of %v (%v)", dp, p, po)) + if depth > 0 { + dp.NotifyDepth(depth) + log.Trace("peer %v notified of new depth limit %v", dp, depth) + } + } + self.conns.EachNeighbourAsync(e, 1024, 255, f, false) +} + +// Off removes a peer from among live peers +func (self *Kademlia) Off(p OverlayConn) { + self.addrs.Swap(p, func(v pot.PotVal) pot.PotVal { + // v cannot be nil, must check otherwise we overwrite entry + if v == nil { + panic(fmt.Sprintf("connected peer not found %v", p)) + } + self.conns.Swap(p, func(v pot.PotVal) pot.PotVal { + // v cannot nil, but no need to check + return nil + }) + return newEntry(p) + }) +} + +// EachConn is an iterator with args (base, po, f) applies f to each live peer +// that has proximity order po or less as measured from the base +// if base is nil, kademlia base address is used +func (self *Kademlia) EachConn(base []byte, o int, f func(OverlayConn, int, bool) bool) { + if len(base) == 0 { + base = self.base + } + p := pot.NewBytesVal(base, nil) + self.conns.EachNeighbour(p, func(val pot.PotVal, po int) bool { + if po > o { + return true + } + isproxbin := false + if l, _ := p.PO(val, 0); l >= self.Depth() { + isproxbin = true + } + return f(val.(OverlayConn), po, isproxbin) + }) +} + +// EachAddr(base, po, f) is an iterator applying f to each known peer +// that has proximity order po or less as measured from the base +// if base is nil, kademlia base address is used +func (self *Kademlia) EachAddr(base []byte, o int, f func(OverlayAddr, int) bool) { + if len(base) == 0 { + base = self.base + } + p := pot.NewBytesVal(base, nil) + self.addrs.EachNeighbour(p, func(val pot.PotVal, po int) bool { + if po > o { + return true + } + return f(val.(OverlayAddr), po) + }) +} + +// Depth returns the proximity order that defines the distance of +// the nearest neighbour set with cardinality >= MinProxBinSize +// if there is altogether less than MinProxBinSize peers it returns 0 +func (self *Kademlia) Depth() (depth int) { + if self.conns.Size() < self.MinProxBinSize { + return 0 + } + var size int + f := func(v pot.PotVal, i int) bool { + size++ + depth = i + return size < self.MinProxBinSize + } + self.conns.EachNeighbour(pot.NewBytesVal(self.base, nil), f) + return depth +} + +func (self *Kademlia) callable(val pot.PotVal) OverlayAddr { + e := val.(*entry) + // not callable if peer is live or exceeded maxRetries + if _, live := val.(OverlayConn); live || e.retries > self.MaxRetries { + log.Trace(fmt.Sprintf("peer %v (%T) not callable", e, e.OverlayPeer)) + return nil + } + // calculate the allowed number of retries based on time lapsed since last seen + timeAgo := time.Since(e.seenAt) + var retries int + for delta := int(timeAgo) / self.RetryInterval; delta > 0; delta /= self.RetryExponent { + log.Trace(fmt.Sprintf("delta: %v", delta)) + retries++ + } + + // this is never called concurrently, so safe to increment + // peer can be retried again + if retries < e.retries { + log.Trace(fmt.Sprintf("log time needed before retry %v, wait only warrants %v", e.retries, retries)) + return nil + } + e.retries++ + log.Trace(fmt.Sprintf("peer %v is callable", e)) + + return val.(OverlayAddr) +} + +func (self *Kademlia) BaseAddr() []byte { + return self.base } // kademlia table + kaddb table displayed with ascii @@ -417,16 +387,16 @@ func (self *Kademlia) String() string { var rows []string rows = append(rows, "=========================================================================") - rows = append(rows, fmt.Sprintf("%v KΛÐΞMLIΛ hive: queen's address: %v", time.Now().UTC().Format(time.UnixDate), fmt.Sprintf("%x", self.addr.OverlayAddr()[:3]))) - rows = append(rows, fmt.Sprintf("population: %d (%d), MinProxBinSize: %d, MinBinSize: %d, MaxBinSize: %d", self.peers.Size(), self.addrs.Size(), self.MinProxBinSize, self.MinBinSize, self.MaxBinSize)) + rows = append(rows, fmt.Sprintf("%v KΛÐΞMLIΛ hive: queen's address: %v", time.Now().UTC().Format(time.UnixDate), self)) + rows = append(rows, fmt.Sprintf("population: %d (%d), MinProxBinSize: %d, MinBinSize: %d, MaxBinSize: %d", self.conns.Size(), self.addrs.Size(), self.MinProxBinSize, self.MinBinSize, self.MaxBinSize)) liverows := make([]string, self.MaxProxDisplay) peersrows := make([]string, self.MaxProxDisplay) - var proxLimit int + var depth int prev := -1 - var proxLimitSet bool - rest := self.peers.Size() - self.peers.EachBin(self.addr, 0, func(po, size int, f func(func(val pot.PotVal, i int) bool) bool) bool { + var depthSet bool + rest := self.conns.Size() + self.conns.EachBin(pot.NewBytesVal(self.base, nil), 0, func(po, size int, f func(func(val pot.PotVal, i int) bool) bool) bool { var rowlen int if po >= self.MaxProxDisplay { po = self.MaxProxDisplay - 1 @@ -434,13 +404,13 @@ func (self *Kademlia) String() string { row := []string{fmt.Sprintf("%2d", size)} rest -= size f(func(val pot.PotVal, vpo int) bool { - row = append(row, val.(*KadPeer).String()[:6]) + row = append(row, val.(*entry).String()[:6]) rowlen++ return rowlen < 4 }) - if !proxLimitSet && (po > prev+1 || rest < self.MinProxBinSize) { - proxLimitSet = true - proxLimit = prev + 1 + if !depthSet && (po > prev+1 || rest < self.MinProxBinSize) { + depthSet = true + depth = prev + 1 } for ; rowlen <= 5; rowlen++ { row = append(row, " ") @@ -450,7 +420,7 @@ func (self *Kademlia) String() string { return true }) - self.addrs.EachBin(self.addr, 0, func(po, size int, f func(func(val pot.PotVal, i int) bool) bool) bool { + self.addrs.EachBin(pot.NewBytesVal(self.base, nil), 0, func(po, size int, f func(func(val pot.PotVal, i int) bool) bool) bool { var rowlen int if po >= self.MaxProxDisplay { po = self.MaxProxDisplay - 1 @@ -461,8 +431,7 @@ func (self *Kademlia) String() string { row := []string{fmt.Sprintf("%2d", size)} // we are displaying live peers too f(func(val pot.PotVal, vpo int) bool { - kp := val.(*KadPeer) - row = append(row, kp.String()[:6]) + row = append(row, val.(*entry).String()[:6]) rowlen++ return rowlen < 4 }) @@ -471,7 +440,7 @@ func (self *Kademlia) String() string { }) for i := 0; i < self.MaxProxDisplay; i++ { - if i == proxLimit { + if i == depth { rows = append(rows, fmt.Sprintf("============ PROX LIMIT: %d ==========================================", i)) } left := liverows[i] @@ -487,3 +456,32 @@ func (self *Kademlia) String() string { rows = append(rows, "=========================================================================") return "\n" + strings.Join(rows, "\n") } + +// Prune implements a forever loop reacting to a ticker time channel given +// as the first argument +// the loop quits if the channel is closed +// it checks each kademlia bin and if the peer count is higher than +// the MaxBinSize parameter it drops the oldest n peers such that +// the bin is reduced to MinBinSize peers thus leaving slots to newly +// connecting peers +func (self *Kademlia) Prune(c <-chan time.Time) { + go func() { + for range c { + total := 0 + self.conns.EachBin(nil, 0, func(po, size int, f func(func(pot.PotVal, int) bool) bool) bool { + extra := size - self.MinBinSize + if size > self.MaxBinSize { + n := 0 + f(func(v pot.PotVal, po int) bool { + v.(OverlayConn).Drop(fmt.Errorf("bucket full")) + n++ + return n < extra + }) + total += extra + } + return true + }) + log.Debug(fmt.Sprintf("pruned %v peers", total)) + } + }() +} diff --git a/swarm/network/kademlia_test.go b/swarm/network/kademlia_test.go index 81d5184bbf..40e1cbac97 100644 --- a/swarm/network/kademlia_test.go +++ b/swarm/network/kademlia_test.go @@ -21,13 +21,12 @@ import ( "testing" "time" - "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/pot" ) -func testKadPeerAddr(s string) *peerAddr { +func testKadPeerAddr(s string) *bzzAddr { a := pot.NewHashAddress(s).Bytes() - return &peerAddr{OAddr: a, UAddr: a} + return &bzzAddr{OAddr: a, UAddr: a} } type testDropPeer struct { @@ -70,7 +69,7 @@ func (self *testDiscPeer) NotifyProx(po uint8) error { return nil } -func (self *testDiscPeer) NotifyPeer(p Peer, po uint8) error { +func (self *testDiscPeer) NotifyPeer(p OverlayPeer, po uint8) error { key := overlayStr(self) key += overlayStr(p) self.lock.Lock() @@ -102,15 +101,15 @@ func newTestKademlia(b string) *testKademlia { } func (k *testKademlia) newTestKadPeer(s string) Peer { - dp := &testDropPeer{&bzzPeer{peerAddr: testKadPeerAddr(s)}, k.dropc} + dp := &testDropPeer{&bzzPeer{bzzAddr: testKadPeerAddr(s)}, k.dropc} if k.Discovery { return Peer(&testDiscPeer{dp, k.lock, k.notifications}) } return Peer(dp) } -func overlayStr(a PeerAddr) string { - log.Error(fmt.Sprintf("PeerAddr: %v (%T)", a, a)) +func overlayStr(a OverlayPeer) string { + // log.Error(fmt.Sprintf("PeerAddr: %v (%T)", a, a)) // if a == (*KadPeer)(nil) || a == (*testDiscPeer)(nil) || a == (*bzzPeer)(nil) || a == nil { // return "" // } @@ -126,14 +125,15 @@ func overlayStr(a PeerAddr) string { // return "" // } // return pot.NewHashAddressFromBytes(p.OverlayAddr()).Bin()[:6] - if a == nil { - return "" - } - k, ok := a.(*KadPeer) - if ok && k.Peer != nil { - return pot.ToBin(a.(*KadPeer).Peer.OverlayAddr())[:6] - } - return pot.ToBin(a.OverlayAddr())[:6] + // if a == nil { + // return "" + // } + // k, ok := a.(*KadPeer) + // if ok && k.Peer != nil { + // return pot.ToBin(a.(*KadPeer).Peer.Over())[:6] + // } + // return pot.ToBin(a.Over())[:6] + return pot.ToBin(a.Address()) } func (k *testKademlia) On(ons ...string) *testKademlia { @@ -146,16 +146,16 @@ func (k *testKademlia) On(ons ...string) *testKademlia { func (k *testKademlia) Off(offs ...string) *testKademlia { for _, s := range offs { - k.Kademlia.Off(k.newTestKadPeer(s)) + k.Kademlia.Off(k.newTestKadPeer(s).(OverlayConn)) } return k } func (k *testKademlia) Register(regs ...string) *testKademlia { - var ps []PeerAddr + var ps []Addr for _, s := range regs { - ps = append(ps, PeerAddr(testKadPeerAddr(s))) + ps = append(ps, Addr(testKadPeerAddr(s))) } k.Kademlia.Register(ps...) return k diff --git a/swarm/network/protocol.go b/swarm/network/protocol.go index 66963215e4..523031db17 100644 --- a/swarm/network/protocol.go +++ b/swarm/network/protocol.go @@ -26,7 +26,6 @@ import ( "github.com/ethereum/go-ethereum/p2p/discover" "github.com/ethereum/go-ethereum/p2p/protocols" "github.com/ethereum/go-ethereum/p2p/simulations/adapters" - "github.com/ethereum/go-ethereum/pot" ) const ( @@ -36,51 +35,68 @@ const ( ProtocolMaxMsgSize = 10 * 1024 * 1024 ) -// bzz is the bzz protocol view of a protocols.Peer (itself an extension of p2p.Peer) -type bzzPeer struct { - *protocols.Peer - localAddr *peerAddr - *peerAddr // remote address - lastActive time.Time +// the Addr interface that peerPool needs +type Addr interface { + OverlayPeer + Over() []byte + Under() []byte + String() string } +// Peer interface represents an live peer connection +type Peer interface { + Addr // the address of a peer + Conn // the live connection (protocols.Peer) + LastActive() time.Time // last time active +} + +// Conn interface represents an live peer connection +type Conn interface { + ID() discover.NodeID // the key that uniquely identifies the Node for the peerPool + Handshake(interface{}, time.Duration) (interface{}, error) // can send messages + Send(interface{}) error // can send messages + Drop(error) // disconnect this peer + Register(interface{}, func(interface{}) error) uint64 // register message-handler callbacks + DisconnectHook(func(error)) // register message-handler callbacks + Run() error // the run function to run a protocol +} + +// bzzPeer is the bzz protocol view of a protocols.Peer (itself an extension of p2p.Peer) +// implements the Peer interface and all interfaces Peer implements: Addr, OverlayPeer +type bzzPeer struct { + Conn // represents the connection for online peers + localAddr *bzzAddr // local Peers address + *bzzAddr // remote address -> implements Addr interface = protocols.Peer + lastActive time.Time // time is updated whenever mutexes are releasing +} + +// Off returns the overlay peer record for offline persistance +func (self *bzzPeer) Off() OverlayAddr { + return self.bzzAddr +} + +// LastActive returns the time the peer was last active func (self *bzzPeer) LastActive() time.Time { return self.lastActive } -// implemented by peerAddr -type PeerAddr interface { - OverlayAddr() []byte - UnderlayAddr() []byte - PO(pot.PotVal, int) (int, bool) - String() string -} - -// the Peer interface that peerPool needs -type Peer interface { - PeerAddr - // String() string // pretty printable the Node - ID() discover.NodeID // the key that uniquely identifies the Node for the peerPool - Send(interface{}) error // can send messages - Drop(error) // disconnect this peer - Register(interface{}, func(interface{}) error) uint64 // register message-handler callbacks - DisconnectHook(func(error)) -} - +// BzzCodeMap compiles the message codes and message types bzz wire protocol. +// note each call to Register can start a new series (initial code is arg1) +// the initial offset for a series is arbitrary (to ensure u) func BzzCodeMap(msgs ...interface{}) *protocols.CodeMap { ct := protocols.NewCodeMap(ProtocolName, Version, ProtocolMaxMsgSize) - ct.Register(&bzzHandshake{}) - ct.Register(msgs...) + ct.Register(0, &bzzHandshake{}) + ct.Register(1, msgs...) return ct } -// Bzz is the protocol constructor +// NewBzz is the protocol constructor // returns p2p.Protocol that is to be offered by the node.Service -func Bzz(oAddr, uAddr []byte, ct *protocols.CodeMap, services func(Peer) error, peerInfo func(id discover.NodeID) interface{}, nodeInfo func() interface{}) *p2p.Protocol { +func NewBzz(over, under []byte, ct *protocols.CodeMap, services func(*bzzPeer) error, peerInfo func(id discover.NodeID) interface{}, nodeInfo func() interface{}) *p2p.Protocol { run := func(p *protocols.Peer) error { bee := &bzzPeer{ - Peer: p, - localAddr: &peerAddr{oAddr, uAddr}, + Conn: p, + localAddr: &bzzAddr{over, under}, } // protocol handshake and its validation // sets remote peer address @@ -115,57 +131,43 @@ func Bzz(oAddr, uAddr []byte, ct *protocols.CodeMap, services func(Peer) error, type bzzHandshake struct { Version uint64 NetworkId uint64 - Addr *peerAddr + Addr *bzzAddr } func (self *bzzHandshake) String() string { return fmt.Sprintf("Handshake: Version: %v, NetworkId: %v, Addr: %v", self.Version, self.NetworkId, self.Addr) } -// peerAddr implements the PeerAddress interface -type peerAddr struct { +// bzzAddr implements the PeerAddr interface +type bzzAddr struct { OAddr []byte UAddr []byte } -func (self *peerAddr) OverlayAddr() []byte { +// implements OverlayPeer interface to be used in pot package +func (self *bzzAddr) Address() []byte { return self.OAddr } -func (self *peerAddr) UnderlayAddr() []byte { +func (self *bzzAddr) Over() []byte { + return self.OAddr +} + +func (self *bzzAddr) Under() []byte { return self.UAddr } -func (self *peerAddr) PO(val pot.PotVal, pos int) (int, bool) { - kp := val.(PeerAddr) - one := kp.OverlayAddr() - other := self.OAddr - for i := pos / 8; i < len(one); i++ { - if one[i] == other[i] { - continue - } - oxo := one[i] ^ other[i] - start := 0 - if i == pos/8 { - start = pos % 8 - } - for j := start; j < 8; j++ { - if (uint8(oxo)>>uint8(7-j))&0x01 != 0 { - return i*8 + j, false - } - } - } - return len(one) * 8, true - // var ha *pot.HashAddress - // var left, right string - // if ok { - // ha = kp.HashAddress - // } else { - // ha = val.(*pot.HashAddress) - // } +func (self *bzzAddr) On(p OverlayConn) OverlayConn { + bp := p.(*bzzPeer) + bp.bzzAddr = self + return bp } -func (self *peerAddr) String() string { +func (self *bzzAddr) Update(a OverlayAddr) OverlayAddr { + return &bzzAddr{self.OAddr, a.(Addr).Under()} +} + +func (self *bzzAddr) String() string { return fmt.Sprintf("%x <%x>", self.OAddr, self.UAddr) } @@ -180,22 +182,20 @@ func (self *bzzPeer) bzzHandshake() error { Addr: self.localAddr, } - hs, err := self.Handshake(lhs) + hs, err := self.Handshake(lhs, time.Second) if err != nil { log.Error(fmt.Sprintf("handshake failed: %v", err)) return err } rhs := hs.(*bzzHandshake) - self.peerAddr = rhs.Addr + self.bzzAddr = rhs.Addr err = checkBzzHandshake(rhs) if err != nil { - log.Error(fmt.Sprintf("handshake between %v and %v failed: %v", self.localAddr, self.peerAddr, err)) + log.Error(fmt.Sprintf("handshake between %v and %v failed: %v", self.localAddr, self.bzzAddr, err)) return err } - return nil - } // checkBzzHandshake checks for the validity and compatibility of the remote handshake @@ -213,7 +213,7 @@ func checkBzzHandshake(rhs *bzzHandshake) error { } // RandomAddr is a utility method generating an address from a public key -func RandomAddr() *peerAddr { +func RandomAddr() *bzzAddr { key, err := crypto.GenerateKey() if err != nil { panic("unable to generate key") @@ -221,23 +221,20 @@ func RandomAddr() *peerAddr { pubkey := crypto.FromECDSAPub(&key.PublicKey) var id discover.NodeID copy(id[:], pubkey[1:]) - return &peerAddr{ + return &bzzAddr{ OAddr: crypto.Keccak256(pubkey[1:]), UAddr: id[:], } } -// NodeId transforms the underlay address to an adapters.NodeId -func NodeId(addr PeerAddr) *adapters.NodeId { - return adapters.NewNodeId(addr.UnderlayAddr()) +// NewNodeIdFromAddr transforms the underlay address to an adapters.NodeId +func NewNodeIdFromAddr(addr Addr) *adapters.NodeId { + return adapters.NewNodeId(addr.Under()) } -// NewPeerAddrFromNodeId constucts a peerAddr from an adapters.NodeId +// NewAddrFromNodeId constucts a bzzAddr from an adapters.NodeId // the overlay address is derived as the hash of the nodeId -func NewPeerAddrFromNodeId(n *adapters.NodeId) *peerAddr { +func NewAddrFromNodeId(n *adapters.NodeId) *bzzAddr { id := n.NodeID - return &peerAddr{ - OAddr: crypto.Keccak256(id[:]), - UAddr: id[:], - } + return &bzzAddr{crypto.Keccak256(id[:]), id[:]} } diff --git a/swarm/network/protocol_test.go b/swarm/network/protocol_test.go index cf750b614d..cc4372a763 100644 --- a/swarm/network/protocol_test.go +++ b/swarm/network/protocol_test.go @@ -34,28 +34,28 @@ func bzzHandshakeExchange(lhs, rhs *bzzHandshake, id *adapters.NodeId) []p2ptest } } -func newBzzBaseTester(t *testing.T, n int, addr *peerAddr, ct *protocols.CodeMap, services func(Peer) error) *bzzTester { +func newBzzBaseTester(t *testing.T, n int, addr *bzzAddr, ct *protocols.CodeMap, services func(*bzzPeer) error) *bzzTester { if ct == nil { ct = BzzCodeMap() } cs := make(map[string]chan bool) - srv := func(p Peer) error { + srv := func(p *bzzPeer) error { defer close(cs[p.ID().String()]) return services(p) } - protocall := Bzz(addr.OverlayAddr(), addr.UnderlayAddr(), ct, srv, nil, nil).Run + protocall := NewBzz(addr.Over(), addr.Under(), ct, srv, nil, nil).Run - s := p2ptest.NewProtocolTester(t, NodeId(addr), n, protocall) + s := p2ptest.NewProtocolTester(t, NewNodeIdFromAddr(addr), n, protocall) for _, id := range s.Ids { cs[id.NodeID.String()] = make(chan bool) } return &bzzTester{ - addr: addr, + addr: addr.Address(), ProtocolTester: s, cs: cs, } @@ -63,13 +63,13 @@ func newBzzBaseTester(t *testing.T, n int, addr *peerAddr, ct *protocols.CodeMap type bzzTester struct { *p2ptest.ProtocolTester - addr *peerAddr + addr []byte cs map[string]chan bool } -func newBzzTester(t *testing.T, n int, addr *peerAddr, pp *p2ptest.TestPeerPool, ct *protocols.CodeMap, services func(Peer) error) *bzzTester { +func newBzzTester(t *testing.T, n int, addr *bzzAddr, pp *p2ptest.TestPeerPool, ct *protocols.CodeMap, services func(Peer) error) *bzzTester { - extraservices := func(p Peer) error { + extraservices := func(p *bzzPeer) error { pp.Add(p) p.DisconnectHook(func(err error) { pp.Remove(p) @@ -88,7 +88,7 @@ func newBzzTester(t *testing.T, n int, addr *peerAddr, pp *p2ptest.TestPeerPool, // should test handshakes in one exchange? parallelisation func (s *bzzTester) testHandshake(lhs, rhs *bzzHandshake, disconnects ...*p2ptest.Disconnect) { var peers []*adapters.NodeId - id := NodeId(rhs.Addr) + id := NewNodeIdFromAddr(rhs.Addr) if len(disconnects) > 0 { for _, d := range disconnects { peers = append(peers, d.Peer) @@ -106,13 +106,13 @@ func (s *bzzTester) runHandshakes(ids ...*adapters.NodeId) { ids = s.Ids } for _, id := range ids { - s.testHandshake(correctBzzHandshake(s.addr), correctBzzHandshake(NewPeerAddrFromNodeId(id))) + s.testHandshake(correctBzzHandshake(s.addr), correctBzzHandshake(NewAddrFromNodeId(id))) <-s.cs[id.NodeID.String()] } } -func correctBzzHandshake(addr *peerAddr) *bzzHandshake { +func correctBzzHandshake(addr *bzzAddr) *bzzHandshake { return &bzzHandshake{0, 322, addr} } @@ -125,7 +125,7 @@ func TestBzzHandshakeNetworkIdMismatch(t *testing.T) { id := s.Ids[0] s.testHandshake( correctBzzHandshake(addr), - &bzzHandshake{0, 321, NewPeerAddrFromNodeId(id)}, + &bzzHandshake{0, 321, NewAddrFromNodeId(id)}, &p2ptest.Disconnect{Peer: id, Error: fmt.Errorf("network id mismatch 321 (!= 322)")}, ) } @@ -139,7 +139,7 @@ func TestBzzHandshakeVersionMismatch(t *testing.T) { id := s.Ids[0] s.testHandshake( correctBzzHandshake(addr), - &bzzHandshake{1, 322, NewPeerAddrFromNodeId(id)}, + &bzzHandshake{1, 322, NewAddrFromNodeId(id)}, &p2ptest.Disconnect{Peer: id, Error: fmt.Errorf("version mismatch 1 (!= 0)")}, ) } @@ -153,7 +153,7 @@ func TestBzzHandshakeSuccess(t *testing.T) { id := s.Ids[0] s.testHandshake( correctBzzHandshake(addr), - &bzzHandshake{0, 322, NewPeerAddrFromNodeId(id)}, + &bzzHandshake{0, 322, NewAddrFromNodeId(id)}, ) } @@ -215,7 +215,7 @@ func TestBzzPeerPoolNotAdd(t *testing.T) { defer s.Stop() id := s.Ids[0] - s.testHandshake(correctBzzHandshake(addr), &bzzHandshake{0, 321, NewPeerAddrFromNodeId(id)}, &p2ptest.Disconnect{Peer: id, Error: fmt.Errorf("network id mismatch 321 (!= 322)")}) + s.testHandshake(correctBzzHandshake(addr), &bzzHandshake{0, 321, NewAddrFromNodeId(id)}, &p2ptest.Disconnect{Peer: id, Error: fmt.Errorf("network id mismatch 321 (!= 322)")}) if pp.Has(id) { t.Fatalf("peer %v incorrectly added: %v", id, pp) } diff --git a/swarm/network/pss.go b/swarm/network/pss.go index 3bd5e8d3ee..fa1265982a 100644 --- a/swarm/network/pss.go +++ b/swarm/network/pss.go @@ -80,7 +80,7 @@ type pssEnvelope struct { TTL uint16 Payload []byte SenderOAddr []byte - SenderUAddr []byte + // SenderUAddr []byte } // Pre-Whisper placeholder @@ -128,6 +128,7 @@ type Pss struct { fwdcache map[pssDigest]pssCacheEntry // checksum of unique fields from pssmsg mapped to expiry, cache to determine whether to drop msg cachettl time.Duration // how long to keep messages in fwdcache hasher func(string) storage.Hasher // hasher to digest message to cache + baseAddr []byte lock sync.Mutex } @@ -135,7 +136,7 @@ func (self *Pss) hashMsg(msg *PssMsg) pssDigest { hasher := self.hasher("SHA3")() hasher.Reset() hasher.Write(msg.GetRecipient()) - hasher.Write(msg.Payload.SenderUAddr) + // hasher.Write(msg.Payload.SenderUAddr) hasher.Write(msg.Payload.SenderOAddr) hasher.Write(msg.Payload.Topic[:]) hasher.Write(msg.Payload.Payload) @@ -147,6 +148,7 @@ func (self *Pss) hashMsg(msg *PssMsg) pssDigest { // // TODO error check overlay integrity func NewPss(k Overlay, params *PssParams) *Pss { + baseAddr := k.BaseAddr() return &Pss{ Overlay: k, //peerPool: make(map[pot.Address]map[PssTopic]*PssReadWriter, PssPeerCapacity), @@ -156,6 +158,7 @@ func NewPss(k Overlay, params *PssParams) *Pss { fwdcache: make(map[pssDigest]pssCacheEntry), cachettl: params.Cachettl, hasher: storage.MakeHashFunc, + baseAddr: baseAddr, } } @@ -301,11 +304,11 @@ func (self *Pss) alertSubscribers(topic *PssTopic, msg []byte) error { func (self *Pss) Send(to []byte, topic PssTopic, msg []byte) error { pssenv := pssEnvelope{ - SenderOAddr: self.Overlay.GetAddr().OverlayAddr(), - SenderUAddr: self.Overlay.GetAddr().UnderlayAddr(), - Topic: topic, - TTL: DefaultTTL, - Payload: msg, + SenderOAddr: self.baseAddr, + // SenderUAddr: self.baseAddr.Under(), + Topic: topic, + TTL: DefaultTTL, + Payload: msg, } pssmsg := &PssMsg{ @@ -328,7 +331,7 @@ func (self *Pss) Forward(msg *PssMsg) error { digest := self.hashMsg(msg) if self.checkFwdCache(nil, digest) { - log.Trace(fmt.Sprintf("pss relay block-cache match: FROM %x TO %x", common.ByteLabel(self.Overlay.GetAddr().OverlayAddr()), common.ByteLabel(msg.GetRecipient()))) + log.Trace(fmt.Sprintf("pss relay block-cache match: FROM %x TO %x", common.ByteLabel(self.baseAddr), common.ByteLabel(msg.GetRecipient()))) //return errorBlockByCache return nil } @@ -339,22 +342,22 @@ func (self *Pss) Forward(msg *PssMsg) error { // send with kademlia // find the closest peer to the recipient and attempt to send - self.Overlay.EachLivePeer(msg.GetRecipient(), 256, func(p Peer, po int, isproxbin bool) bool { - if self.checkFwdCache(p.OverlayAddr(), digest) { - log.Warn(fmt.Sprintf("BOUNCE DEFER PSS-relay FROM %x TO %x THRU %x:", common.ByteLabel(self.Overlay.GetAddr().OverlayAddr()), common.ByteLabel(msg.GetRecipient()), common.ByteLabel(p.OverlayAddr()))) + self.Overlay.EachConn(msg.GetRecipient(), 256, func(p OverlayConn, po int, isproxbin bool) bool { + if self.checkFwdCache(p.Address(), digest) { + log.Warn(fmt.Sprintf("BOUNCE DEFER PSS-relay FROM %x TO %x THRU %x:", common.ByteLabel(self.baseAddr), common.ByteLabel(msg.GetRecipient()), common.ByteLabel(p.Address()))) return true } - log.Warn(fmt.Sprintf("Attempting PSS-relay FROM %x TO %x THRU %x", common.ByteLabel(self.Overlay.GetAddr().OverlayAddr()), common.ByteLabel(msg.GetRecipient()), common.ByteLabel(p.OverlayAddr()))) - err := p.Send(msg) + log.Warn(fmt.Sprintf("Attempting PSS-relay FROM %x TO %x THRU %x", common.ByteLabel(self.baseAddr), common.ByteLabel(msg.GetRecipient()), common.ByteLabel(p.Address()))) + err := p.(Peer).Send(msg) if err != nil { - log.Warn(fmt.Sprintf("FAILED PSS-relay FROM %x TO %x THRU %x: %v", common.ByteLabel(self.Overlay.GetAddr().OverlayAddr()), common.ByteLabel(msg.GetRecipient()), common.ByteLabel(p.OverlayAddr()), err)) + log.Warn(fmt.Sprintf("FAILED PSS-relay FROM %x TO %x THRU %x: %v", common.ByteLabel(self.baseAddr), common.ByteLabel(msg.GetRecipient()), common.ByteLabel(p.Address()), err)) return true } sent++ - if bytes.Equal(msg.GetRecipient(), p.OverlayAddr()) || !isproxbin { + if bytes.Equal(msg.GetRecipient(), p.Address()) || !isproxbin { return false } - log.Trace(fmt.Sprintf("%x is in proxbin, so we continue sending", common.ByteLabel(p.OverlayAddr()))) + log.Trace(fmt.Sprintf("%x is in proxbin, so we continue sending", common.ByteLabel(p.Address()))) return true }) if sent == 0 { @@ -469,7 +472,7 @@ func (self *PssProtocol) handle(msg []byte, p *p2p.Peer, senderAddr []byte) erro } func (self *Pss) IsSelfRecipient(msg *PssMsg) bool { - if bytes.Equal(msg.GetRecipient(), self.Overlay.GetAddr().OverlayAddr()) { + if bytes.Equal(msg.GetRecipient(), self.baseAddr) { return true } return false diff --git a/swarm/network/pss_test.go b/swarm/network/pss_test.go index c0e385c4a7..68b3f2abfa 100644 --- a/swarm/network/pss_test.go +++ b/swarm/network/pss_test.go @@ -59,19 +59,15 @@ type pssTestNode struct { apifunc func() []rpc.API } -func (n *pssTestNode) Add(peer Peer) error { +func (n *pssTestNode) Add(peer *bzzPeer) error { err := n.Hive.Add(peer) time.Sleep(time.Millisecond * 250) n.triggerCheck() return err } -func (n *pssTestNode) Remove(peer Peer) { - n.Hive.Remove(peer) -} - func (n *pssTestNode) hiveKeepAlive() <-chan time.Time { - return time.Tick(time.Second * 10) + return time.Tick(time.Millisecond * 300) } func (n *pssTestNode) triggerCheck() { @@ -79,7 +75,7 @@ func (n *pssTestNode) triggerCheck() { } func (n *pssTestNode) OverlayAddr() []byte { - return n.Pss.Overlay.GetAddr().OverlayAddr() + return n.Pss.Overlay.BaseAddr() } func (n *pssTestNode) UnderlayAddr() []byte { @@ -112,7 +108,7 @@ func newPssTestService(t *testing.T, handlefunc func(interface{}) error, testnod } func (self *pssTestService) Start(server p2p.Server) error { - return self.node.Hive.Start(server, self.node.hiveKeepAlive) + return self.node.Hive.Start(server, self.node.hiveKeepAlive, nil) } func (self *pssTestService) Stop() error { @@ -122,12 +118,12 @@ func (self *pssTestService) Stop() error { func (self *pssTestService) Protocols() []p2p.Protocol { ct := BzzCodeMap() + ct.Register(0, &PssMsg{}) for _, m := range DiscoveryMsgs { - ct.Register(m) + ct.Register(1, m) } - ct.Register(&PssMsg{}) - srv := func(p Peer) error { + srv := func(p *bzzPeer) error { p.Register(&PssMsg{}, self.msgFunc) self.node.Add(p) p.DisconnectHook(func(err error) { @@ -136,7 +132,7 @@ func (self *pssTestService) Protocols() []p2p.Protocol { return nil } - proto := Bzz(self.node.OverlayAddr(), self.node.UnderlayAddr(), ct, srv, nil, nil) + proto := NewBzz(self.node.OverlayAddr(), self.node.UnderlayAddr(), ct, srv, nil, nil) return []p2p.Protocol{*proto} } @@ -167,9 +163,9 @@ func TestPssCache(t *testing.T) { Payload: pssEnvelope{ TTL: 0, SenderOAddr: oaddr, - SenderUAddr: uaddr, - Topic: topic, - Payload: data, + // SenderUAddr: uaddr, + Topic: topic, + Payload: data, }, } msg.SetRecipient(to) @@ -177,10 +173,10 @@ func TestPssCache(t *testing.T) { msgtwo := &PssMsg{ Payload: pssEnvelope{ TTL: 0, - SenderOAddr: uaddr, - SenderUAddr: oaddr, - Topic: topic, - Payload: data, + SenderOAddr: oaddr, + // SenderUAddr: oaddr, + Topic: topic, + Payload: data, }, } msgtwo.SetRecipient(to) @@ -197,16 +193,16 @@ func TestPssCache(t *testing.T) { } // check the sender cache - err = ps.addFwdCacheSender(fwdaddr.OverlayAddr(), digest) + err = ps.addFwdCacheSender(fwdaddr.Over(), digest) if err != nil { t.Fatalf("write to pss sender cache failed: %v", err) } - if !ps.checkFwdCache(fwdaddr.OverlayAddr(), digest) { + if !ps.checkFwdCache(fwdaddr.Over(), digest) { t.Fatalf("message %v should have SENDER record in cache but checkCache returned false", msg) } - if ps.checkFwdCache(fwdaddr.OverlayAddr(), digesttwo) { + if ps.checkFwdCache(fwdaddr.Over(), digesttwo) { t.Fatalf("message %v should NOT have SENDER record in cache but checkCache returned true", msgtwo) } @@ -229,12 +225,12 @@ func TestPssCache(t *testing.T) { t.Fatalf("message %v should have expired from cache but checkCache returned true", msg) } - err = ps.AddToCache(fwdaddr.OverlayAddr(), msgtwo) + err = ps.AddToCache(fwdaddr.Over(), msgtwo) if err != nil { t.Fatalf("public accessor cache write failed: %v", err) } - if !ps.checkFwdCache(fwdaddr.OverlayAddr(), digesttwo) { + if !ps.checkFwdCache(fwdaddr.Over(), digesttwo) { t.Fatalf("message %v should have SENDER record in cache but checkCache returned false", msgtwo) } } @@ -243,7 +239,7 @@ func TestPssRegisterHandler(t *testing.T) { var topic PssTopic var err error addr := RandomAddr() - ps := makePss(addr.UnderlayAddr()) + ps := makePss(addr.Under()) topic, _ = MakeTopic(protocolName, protocolVersion) err = ps.Register(topic, func(msg []byte, p *p2p.Peer, sender []byte) error { return nil }) @@ -305,7 +301,8 @@ func testPssFullRandom(t *testing.T, numsends int, numnodes int, numfullnodes in expectnodesids := []*adapters.NodeId{} // the nodes to expect on (needed by checker) expectnodesresults := make(map[*adapters.NodeId][]int) // which messages expect actually got - vct := protocols.NewCodeMap(protocolName, protocolVersion, 65535, &pssTestPayload{}) + vct := protocols.NewCodeMap(protocolName, protocolVersion, ProtocolMaxMsgSize) + vct.Register(0, &pssTestPayload{}) topic, _ := MakeTopic(protocolName, protocolVersion) trigger := make(chan *adapters.NodeId) @@ -471,8 +468,8 @@ func testPssFullRandom(t *testing.T, numsends int, numnodes int, numfullnodes in for i := 0; i < len(sends); i += 2 { t.Logf("Pss #%d: oaddr %x -> %x (uaddr %x -> %x)", i/2+1, - common.ByteLabel(nodes[fullnodes[sends[i]]].Pss.GetAddr().OverlayAddr()), - common.ByteLabel(nodes[fullnodes[sends[i+1]]].Pss.GetAddr().OverlayAddr()), + common.ByteLabel(nodes[fullnodes[sends[i]]].Pss.BaseAddr()), + common.ByteLabel(nodes[fullnodes[sends[i+1]]].Pss.BaseAddr()), common.ByteLabel(fullnodes[sends[i]].Bytes()), common.ByteLabel(fullnodes[sends[i+1]].Bytes())) } @@ -484,15 +481,15 @@ func testPssFullRandom(t *testing.T, numsends int, numnodes int, numfullnodes in fails++ } } - t.Logf("Node oaddr %x (uaddr %x) was sent %d msgs, of which %d failed", common.ByteLabel(nodes[id].Pss.GetAddr().OverlayAddr()), common.ByteLabel(id.Bytes()), len(results), fails) + t.Logf("Node oaddr %x (uaddr %x) was sent %d msgs, of which %d failed", common.ByteLabel(nodes[id].Pss.BaseAddr()), common.ByteLabel(id.Bytes()), len(results), fails) totalfails += fails } t.Logf("Total sent: %d, total fail: %d (%.2f%%)", len(sends)/2, totalfails, (float32(totalfails)/float32(len(sends)/2+1))*100) for _, node := range nodes { - logstring := fmt.Sprintf("Node oaddr %x kademlia: ", common.ByteLabel(node.Pss.Overlay.GetAddr().OverlayAddr())) - node.Pss.Overlay.EachLivePeer(nil, 256, func(p Peer, po int, isprox bool) bool { - logstring += fmt.Sprintf("%x ", common.ByteLabel(p.OverlayAddr())) + logstring := fmt.Sprintf("Node oaddr %x kademlia: ", common.ByteLabel(node.Pss.Overlay.BaseAddr())) + node.Pss.Overlay.EachConn(nil, 256, func(p Peer, po int, isprox bool) bool { + logstring += fmt.Sprintf("%x ", common.ByteLabel(p.Over())) return true }) t.Log(logstring) @@ -511,7 +508,8 @@ func TestPssFullLinearEcho(t *testing.T) { var firstpssnode *adapters.NodeId var secondpssnode *adapters.NodeId - vct := protocols.NewCodeMap(protocolName, protocolVersion, 65535, &pssTestPayload{}) + vct := protocols.NewCodeMap(protocolName, protocolVersion, ProtocolMaxMsgSize) + vct.Register(0, &pssTestPayload{}) topic, _ := MakeTopic(protocolName, protocolVersion) fullnodes := []*adapters.NodeId{} @@ -559,9 +557,8 @@ func TestPssFullLinearEcho(t *testing.T) { node, ok := nodes[id] if !ok { return false, fmt.Errorf("unknown node: %s (%v)", id, node) - } else { - log.Trace(fmt.Sprintf("sim check ok node %v", id)) } + log.Trace(fmt.Sprintf("sim check ok node %v", id)) return true, nil } @@ -589,7 +586,7 @@ func TestPssFullLinearEcho(t *testing.T) { // first find a node that we're connected to for firstpssnode == nonode { log.Debug(fmt.Sprintf("Waiting for pss relaypeer for %x close to %x ...", common.ByteLabel(nodes[fullnodes[0]].OverlayAddr()), common.ByteLabel(nodes[ids[1]].OverlayAddr()))) - nodes[fullnodes[0]].Pss.Overlay.EachLivePeer(nodes[fullnodes[1]].OverlayAddr(), 256, func(p Peer, po int, isprox bool) bool { + nodes[fullnodes[0]].Pss.Overlay.EachConn(nodes[fullnodes[1]].OverlayAddr(), 256, func(p Peer, po int, isprox bool) bool { for _, id := range ids { if id.NodeID == p.ID() { firstpssnode = id @@ -609,7 +606,7 @@ func TestPssFullLinearEcho(t *testing.T) { // then find the node it's connected to for secondpssnode == nonode { log.Debug(fmt.Sprintf("PSS kademlia: Waiting for recipientpeer for %x close to %x ...", common.ByteLabel(nodes[firstpssnode].OverlayAddr()), common.ByteLabel(nodes[fullnodes[1]].OverlayAddr()))) - nodes[firstpssnode].Pss.Overlay.EachLivePeer(nodes[fullnodes[1]].OverlayAddr(), 256, func(p Peer, po int, isprox bool) bool { + nodes[firstpssnode].Pss.Overlay.Eachc(nodes[fullnodes[1]].OverlayAddr(), 256, func(p Peer, po int, isprox bool) bool { for _, id := range ids { if id.NodeID == p.ID() && id.NodeID != fullnodes[0].NodeID { secondpssnode = id @@ -692,7 +689,8 @@ func TestPssFullWS(t *testing.T) { var firstpssnode, secondpssnode *adapters.NodeId fullnodes := []*adapters.NodeId{} - vct := protocols.NewCodeMap(protocolName, protocolVersion, 65535, &pssTestPayload{}) + vct := protocols.NewCodeMap(protocolName, protocolVersion, ProtocolMaxMsgSize) + vct.Register(0, &pssTestPayload{}) topic, _ := MakeTopic(pingTopicName, pingTopicVersion) trigger := make(chan *adapters.NodeId) @@ -788,7 +786,7 @@ func TestPssFullWS(t *testing.T) { // then find the node it's connected to for secondpssnode == nonode { log.Debug(fmt.Sprintf("PSS kademlia: Waiting for recipientpeer for %x close to %x ...", common.ByteLabel(nodes[firstpssnode].OverlayAddr()), common.ByteLabel(nodes[fullnodes[1]].OverlayAddr()))) - nodes[firstpssnode].Pss.Overlay.EachLivePeer(nodes[fullnodes[1]].OverlayAddr(), 256, func(p Peer, po int, isprox bool) bool { + nodes[firstpssnode].Pss.Overlay.EachConn(nodes[fullnodes[1]].OverlayAddr(), 256, func(p Peer, po int, isprox bool) bool { for _, id := range ids { if id.NodeID == p.ID() && id.NodeID != fullnodes[0].NodeID { secondpssnode = id @@ -855,7 +853,7 @@ func TestPssFullWS(t *testing.T) { action = func(ctx context.Context) error { go func() { clientrecv.EthSubscribe(ctx, ch, "newMsg", topic) - clientsend.Call(nil, "eth_sendRaw", nodes[secondpssnode].Pss.Overlay.GetAddr().OverlayAddr(), topic, []byte("ping")) + clientsend.Call(nil, "eth_sendRaw", nodes[secondpssnode].Pss.Overlay.BaseAddr(), topic, []byte("ping")) trigger <- secondpssnode }() return nil @@ -935,7 +933,7 @@ func newPssSimulationTester(t *testing.T, numnodes int, numfullnodes int, trigge if testpeers[id] != nil { handlefunc = makePssHandleProtocol(psss[id]) - log.Trace(fmt.Sprintf("Making full protocol id %x addr %x (testpeers %p)", common.ByteLabel(id.Bytes()), common.ByteLabel(addr.OverlayAddr()), testpeers)) + log.Trace(fmt.Sprintf("Making full protocol id %x addr %x (testpeers %p)", common.ByteLabel(id.Bytes()), common.ByteLabel(addr.Over()), testpeers)) } else { handlefunc = makePssHandleForward(psss[id]) } @@ -965,7 +963,7 @@ func newPssSimulationTester(t *testing.T, numnodes int, numfullnodes int, trigge } for i, conf := range configs { addr := NewPeerAddrFromNodeId(conf.Id) - psss[conf.Id] = makePss(addr.OverlayAddr()) + psss[conf.Id] = makePss(addr.Over()) if i < numfullnodes { tp := &pssTestPeer{ Peer: &protocols.Peer{ @@ -1018,7 +1016,7 @@ func makeCustomProtocol(name string, version int, ct *protocols.CodeMap, testpee return protocols.NewProtocol(name, uint(version), run, ct, nil, nil) } -func makeFakeMsg(ps *Pss, ct *protocols.CodeMap, topic PssTopic, senderaddr PeerAddr, content string) PssMsg { +func makeFakeMsg(ps *Pss, ct *protocols.CodeMap, topic PssTopic, senderaddr Addr, content string) PssMsg { data := pssTestPayload{} code, found := ct.GetCode(&data) if !found { @@ -1033,8 +1031,8 @@ func makeFakeMsg(ps *Pss, ct *protocols.CodeMap, topic PssTopic, senderaddr Peer } pssenv := pssEnvelope{ - SenderOAddr: senderaddr.OverlayAddr(), - SenderUAddr: senderaddr.UnderlayAddr(), + SenderOAddr: senderaddr.Over(), + SenderUAddr: senderaddr.Under(), Topic: topic, TTL: DefaultTTL, Payload: rlpbundle, @@ -1042,7 +1040,7 @@ func makeFakeMsg(ps *Pss, ct *protocols.CodeMap, topic PssTopic, senderaddr Peer pssmsg := PssMsg{ Payload: pssenv, } - pssmsg.SetRecipient(ps.Overlay.GetAddr().OverlayAddr()) + pssmsg.SetRecipient(ps.Overlay.BaseAddr()) return pssmsg } diff --git a/swarm/network/simulations/overlay.go b/swarm/network/simulations/overlay.go index 198de66394..1fe613676b 100644 --- a/swarm/network/simulations/overlay.go +++ b/swarm/network/simulations/overlay.go @@ -24,10 +24,31 @@ import ( // SimNode is the adapter used by Swarm simulations. type SimNode struct { + id *adapters.NodeId + rw network.ReadWriter hive *network.Hive protocol *p2p.Protocol } +type simReadWriter struct { + m map[string][]byte +} + +func (self *simReadWriter) ReadAll(s string) ([]byte, error) { + return self.m[s], nil +} + +func (self *simReadWriter) WriteAll(s string, data []byte) error { + self.m[s] = data + return nil +} + +func NewSimReadWriter() *simReadWriter { + return &simReadWriter{ + make(map[string][]byte), + } +} + func (s *SimNode) Protocols() []p2p.Protocol { return []p2p.Protocol{*s.protocol} } @@ -44,19 +65,12 @@ func af() <-chan time.Time { // Start() starts up the hive // makes SimNode implement node.Service func (self *SimNode) Start(server p2p.Server) error { - return self.hive.Start(server, af) + self.init() + return self.hive.Start(server, af, self.rw) } -// Stop() shuts down the hive -// makes SimNode implement node.Service -func (self *SimNode) Stop() error { - self.hive.Stop() - return nil -} - -// NewSimNode creates adapters for nodes in the simulation. -func NewSimNode(id *adapters.NodeId, snapshot []byte) node.Service { - addr := network.NewPeerAddrFromNodeId(id) +func (self *SimNode) init() { + addr := network.NewPeerAddrFromNodeId(self.id) kp := network.NewKadParams() kp.MinProxBinSize = 2 @@ -83,11 +97,25 @@ func NewSimNode(id *adapters.NodeId, snapshot []byte) node.Service { ct := network.BzzCodeMap(network.DiscoveryMsgs...) // bzz protocol code map nodeInfo := func() interface{} { return pp.String() } + self.hive = pp + self.protocol = network.Bzz(addr.OverlayAddr(), addr.UnderlayAddr(), ct, services, nil, nodeInfo) +} - return &SimNode{ - hive: pp, - protocol: network.Bzz(addr.OverlayAddr(), addr.UnderlayAddr(), ct, services, nil, nodeInfo), +// Stop() shuts down the hive +// makes SimNode implement node.Service +func (self *SimNode) Stop() error { + self.hive.Stop() + return nil +} + +// NewSimNode creates adapters for nodes in the simulation. +func NewSimNode(id *adapters.NodeId, snapshot []byte) node.Service { + s := &SimNode{ + id: id, + rw: NewSimReadWriter(), } + s.init() + return s } func createMockers() map[string]*simulations.MockerConfig { diff --git a/swarm/network/test_overlay.go b/swarm/network/test_overlay.go index de839bcbab..ebcbbc3108 100644 --- a/swarm/network/test_overlay.go +++ b/swarm/network/test_overlay.go @@ -1,182 +1,193 @@ package network -import ( - "fmt" - "strings" - "sync" - - "github.com/ethereum/go-ethereum/log" -) - -const orders = 8 - -type testOverlay struct { - mu sync.Mutex - addr []byte - pos [][]*testPeerAddr - posMap map[string]*testPeerAddr -} - -type testPeerAddr struct { - PeerAddr - Peer Peer -} - -func (self *testOverlay) Register(nas ...PeerAddr) error { - self.mu.Lock() - defer self.mu.Unlock() - return self.register(nas...) -} - -func (self *testOverlay) GetAddr() PeerAddr { - return &peerAddr{ - OAddr: self.addr, - UAddr: []byte{}, - } -} - -func (self *testOverlay) register(nas ...PeerAddr) error { - for _, na := range nas { - tna := &testPeerAddr{PeerAddr: na} - addr := na.OverlayAddr() - if self.posMap[string(addr)] != nil { - continue - } - self.posMap[string(addr)] = tna - o := order(addr) - log.Trace(fmt.Sprintf("PO: %v, orders: %v", o, orders)) - self.pos[o] = append(self.pos[o], tna) - } - return nil -} - -func order(addr []byte) int { - return int(addr[0]) / 32 -} - -func (self *testOverlay) On(n Peer) { - self.mu.Lock() - defer self.mu.Unlock() - addr := n.OverlayAddr() - na := self.posMap[string(addr)] - if na == nil { - self.register(n) - na = self.posMap[string(addr)] - } else if na.Peer != nil { - return - } - log.Trace(fmt.Sprintf("Online: %x", addr[:4])) - na.Peer = n - return -} - -func (self *testOverlay) Off(n Peer) { - self.mu.Lock() - defer self.mu.Unlock() - addr := n.OverlayAddr() - na := self.posMap[string(addr)] - if na == nil { - return - } - delete(self.posMap, string(addr)) - na.Peer = nil -} - -// caller must hold the lock -func (self *testOverlay) on(po []*testPeerAddr) (nodes []Peer) { - for _, na := range po { - if na.Peer != nil { - nodes = append(nodes, na.Peer) - } - } - return nodes -} - -// caller must hold the lock -func (self *testOverlay) off(po []*testPeerAddr) (nas []PeerAddr) { - for _, na := range po { - if na.Peer == (*bzzPeer)(nil) { - nas = append(nas, PeerAddr(na)) - } - } - return nas -} - -func (self *testOverlay) EachLivePeer(base []byte, o int, f func(Peer, int, bool) bool) { - if base == nil { - base = self.addr - } - for i := o; i < len(self.pos); i++ { - for _, na := range self.pos[i] { - if na.Peer != nil { - if !f(na.Peer, o, false) { - return - } - } - } - } -} - -func (self *testOverlay) EachPeer(base []byte, o int, f func(PeerAddr, int) bool) { - if base == nil { - base = self.addr - } - for i := o; i < len(self.pos); i++ { - for _, na := range self.pos[i] { - if !f(na, i) { - return - } - } - } -} - -func (self *testOverlay) SuggestPeer() (PeerAddr, int, bool) { - self.mu.Lock() - defer self.mu.Unlock() - for i, po := range self.pos { - ons := self.on(po) - if len(ons) < 2 { - offs := self.off(po) - if len(offs) > 0 { - log.Trace(fmt.Sprintf("node %v is off", offs[0])) - return offs[0], i, true - } - } - } - return nil, 0, true -} - -func (self *testOverlay) String() string { - self.mu.Lock() - defer self.mu.Unlock() - var t []string - var ons, offs int - var ns []Peer - var nas []PeerAddr - for o, po := range self.pos { - var row []string - ns = self.on(po) - nas = self.off(po) - ons = len(ns) - for _, n := range ns { - addr := n.OverlayAddr() - row = append(row, fmt.Sprintf("%x", addr[:4])) - } - row = append(row, "|") - offs = len(nas) - for _, na := range nas { - addr := na.OverlayAddr() - row = append(row, fmt.Sprintf("%x", addr[:4])) - } - t = append(t, fmt.Sprintf("%v: (%v/%v) %v", o, ons, offs, strings.Join(row, " "))) - } - return strings.Join(t, "\n") -} - -func NewTestOverlay(addr []byte) *testOverlay { - return &testOverlay{ - addr: addr, - posMap: make(map[string]*testPeerAddr), - pos: make([][]*testPeerAddr, orders), - } -} +// +// import ( +// "fmt" +// "strings" +// "sync" +// +// "github.com/ethereum/go-ethereum/log" +// ) +// +// const orders = 8 +// +// type testOverlay struct { +// mu sync.Mutex +// addr []byte +// pos [][]OverlayAddr +// posMap map[string]OverlayAddr +// } +// +// type testPeerAddr struct { +// Addr +// Peer +// } +// +// func (self *testPeerAddr) Address() []byte { +// return nil +// } +// +// func (self *testPeerAddr) Update(a OverlayAddr) OverlayAddr { +// return self +// } +// +// func (self *testPeerAddr) On(p OverlayConn) OverlayConn { +// return self +// } +// +// func (self *testPeerAddr) Off() OverlayAddr { +// return self +// } +// +// func (self *testOverlay) Register(peers chan OverlayAddr) error { +// self.mu.Lock() +// defer self.mu.Unlock() +// var nas []OverlayAddr +// for a := range peers { +// nas = append(nas, a) +// } +// return self.register(nas...) +// } +// +// func (self *testOverlay) BaseAddr() []byte { +// return nil +// } +// +// func (self *testOverlay) register(nas ...OverlayAddr) error { +// for _, na := range nas { +// addr := na.Address() +// if self.posMap[string(addr)] != nil { +// continue +// } +// self.posMap[string(addr)] = na +// o := order(addr) +// log.Trace(fmt.Sprintf("PO: %v, orders: %v", o, orders)) +// self.pos[o] = append(self.pos[o], na) +// } +// return nil +// } +// +// func order(addr []byte) int { +// return int(addr[0]) / 32 +// } +// +// func (self *testOverlay) On(n OverlayConn) { +// self.mu.Lock() +// defer self.mu.Unlock() +// addr := n.Address() +// na := self.posMap[string(addr)] +// if na == nil { +// self.register(n) +// na = self.posMap[string(addr)] +// } else if na.Peer != nil { +// return +// } +// log.Trace(fmt.Sprintf("Online: %x", addr[:4])) +// na.Peer = n +// return +// } +// +// func (self *testOverlay) Off(n OverlayConn) { +// self.mu.Lock() +// defer self.mu.Unlock() +// addr := n.Over() +// na := self.posMap[string(addr)] +// if na == nil { +// return +// } +// delete(self.posMap, string(addr)) +// na.Peer = nil +// } +// +// // caller must hold the lock +// func (self *testOverlay) on(po []*testPeerAddr) (nodes []OverlayConn) { +// for _, na := range po { +// if na.Peer != nil { +// nodes = append(nodes, na) +// } +// } +// return nodes +// } +// +// // caller must hold the lock +// func (self *testOverlay) off(po []*testPeerAddr) (nas []OverlayAddr) { +// for _, na := range po { +// if na.Peer == (*bzzPeer)(nil) { +// nas = append(nas, Addr(na)) +// } +// } +// return nas +// } +// +// func (self *testOverlay) EachConn(base []byte, o int, f func(OverlayConn, int, bool) bool) { +// for i := o; i < len(self.pos); i++ { +// for _, na := range self.pos[i] { +// if na.Peer != nil { +// if !f(na, o, false) { +// return +// } +// } +// } +// } +// } +// +// func (self *testOverlay) EachAddr(base []byte, o int, f func(OverlayAddr, int) bool) { +// for i := o; i < len(self.pos); i++ { +// for _, na := range self.pos[i] { +// if !f(na, i) { +// return +// } +// } +// } +// } +// +// func (self *testOverlay) SuggestPeer() (OverlayAddr, int, bool) { +// self.mu.Lock() +// defer self.mu.Unlock() +// for i, po := range self.pos { +// ons := self.on(po) +// if len(ons) < 2 { +// offs := self.off(po) +// if len(offs) > 0 { +// log.Trace(fmt.Sprintf("node %v is off", offs[0])) +// return offs[0], i, true +// } +// } +// } +// return nil, 0, true +// } +// +// func (self *testOverlay) String() string { +// self.mu.Lock() +// defer self.mu.Unlock() +// var t []string +// var ons, offs int +// var ns []Peer +// var nas []Addr +// for o, po := range self.pos { +// var row []string +// ns = self.on(po) +// nas = self.off(po) +// ons = len(ns) +// for _, n := range ns { +// addr := n.Over() +// row = append(row, fmt.Sprintf("%x", addr[:4])) +// } +// row = append(row, "|") +// offs = len(nas) +// for _, na := range nas { +// addr := na.Over() +// row = append(row, fmt.Sprintf("%x", addr[:4])) +// } +// t = append(t, fmt.Sprintf("%v: (%v/%v) %v", o, ons, offs, strings.Join(row, " "))) +// } +// return strings.Join(t, "\n") +// } +// +// func NewTestOverlay(addr []byte) *testOverlay { +// return &testOverlay{ +// addr: addr, +// posMap: make(map[string]*testPeerAddr), +// pos: make([][]*testPeerAddr, orders), +// } +// } diff --git a/swarm/swarm.go b/swarm/swarm.go index c900f23b61..a8b6353dd9 100644 --- a/swarm/swarm.go +++ b/swarm/swarm.go @@ -32,7 +32,6 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/p2p" - "github.com/ethereum/go-ethereum/p2p/simulations/adapters" "github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/swarm/api" httpapi "github.com/ethereum/go-ethereum/swarm/api/http" @@ -182,9 +181,10 @@ func (self *Swarm) Start(net p2p.Server) error { func() <-chan time.Time { return time.NewTicker(time.Second).C }, + nil, ) - log.Info(fmt.Sprintf("Swarm network started on bzz address: %v", self.hive.GetAddr())) + log.Info(fmt.Sprintf("Swarm network started on bzz address: %v", self.hive.BaseAddr())) if self.pssEnabled { pssparams := network.NewPssParams() @@ -238,48 +238,46 @@ func (self *Swarm) Stop() error { // implements the node.Service interface func (self *Swarm) Protocols() []p2p.Protocol { ct := network.BzzCodeMap() - for _, m := range network.DiscoveryMsgs { - ct.Register(m) - } if self.pssEnabled { - ct.Register(&network.PssMsg{}) + ct.Register(1, &network.PssMsg{}) } + ct.Register(2, network.DiscoveryMsgs...) - srv := func(p network.Peer) error { - if self.pssEnabled { - p.Register(&network.PssMsg{}, func(msg interface{}) error { - pssmsg := msg.(*network.PssMsg) - - if self.pss.IsSelfRecipient(pssmsg) { - log.Trace("pss for us, yay! ... let's process!") - env := pssmsg.Payload - umsg := env.Payload - f := self.pss.GetHandler(env.Topic) - if f == nil { - return fmt.Errorf("No registered handler for topic '%s'", env.Topic) - } - nid := adapters.NewNodeId(env.SenderUAddr) - p := p2p.NewPeer(nid.NodeID, fmt.Sprintf("%x", common.ByteLabel(nid.Bytes())), []p2p.Cap{}) - return f(umsg, p, env.SenderOAddr) - } else { - log.Trace("pss was for someone else :'( ... forwarding") - return self.pss.Forward(pssmsg) - } - return nil - }) - } - self.hive.Add(p) - p.DisconnectHook(func(err error) { - self.hive.Remove(p) - }) - return nil - } + // srv := func(p network.Peer) error { + // if self.pssEnabled { + // p.Register(&network.PssMsg{}, func(msg interface{}) error { + // pssmsg := msg.(*network.PssMsg) + // + // if self.pss.IsSelfRecipient(pssmsg) { + // log.Trace("pss for us, yay! ... let's process!") + // env := pssmsg.Payload + // umsg := env.Payload + // f := self.pss.GetHandler(env.Topic) + // if f == nil { + // return fmt.Errorf("No registered handler for topic '%s'", env.Topic) + // } + // nid := adapters.NewNodeId(env.SenderUAddr) + // p := p2p.NewPeer(nid.NodeID, fmt.Sprintf("%x", common.ByteLabel(nid.Bytes())), []p2p.Cap{}) + // return f(umsg, p, env.SenderOAddr) + // } else { + // log.Trace("pss was for someone else :'( ... forwarding") + // return self.pss.Forward(pssmsg) + // } + // return nil + // }) + // } + // self.hive.Add(p) + // p.DisconnectHook(func(err error) { + // self.hive.Remove(p) + // }) + // return nil + // } proto := network.Bzz( - self.hive.Overlay.GetAddr().OverlayAddr(), - self.hive.Overlay.GetAddr().UnderlayAddr(), + self.hive.Overlay.GetAddr().Over(), + self.hive.Overlay.GetAddr().Under(), ct, - srv, + nil, nil, nil, ) From 91c198778cf9714d7416d8aef377681a666c9450 Mon Sep 17 00:00:00 2001 From: Lewis Marshall Date: Sun, 14 May 2017 00:40:32 -0700 Subject: [PATCH 03/24] p2p/protocols: Refactor Signed-off-by: Lewis Marshall --- p2p/dial.go | 18 +- p2p/dial_test.go | 4 +- p2p/protocols/protocol.go | 300 ++++++--------- p2p/protocols/protocol_test.go | 79 ++-- p2p/server.go | 9 +- p2p/simulations/adapters/inproc.go | 348 +++++------------- p2p/testing/protocolsession.go | 2 +- p2p/testing/protocoltester.go | 11 +- swarm/network/discovery.go | 48 +-- swarm/network/discovery_test.go | 14 +- swarm/network/hive.go | 81 ++-- swarm/network/hive_test.go | 53 +-- swarm/network/kademlia_test.go | 13 +- swarm/network/protocol.go | 313 +++++++++++----- swarm/network/protocol_test.go | 79 ++-- swarm/network/pss.go | 35 +- swarm/network/pss_test.go | 83 ++--- .../simulations/discovery/discovery_test.go | 91 ++--- 18 files changed, 737 insertions(+), 844 deletions(-) diff --git a/p2p/dial.go b/p2p/dial.go index b832fc233e..10f16f5166 100644 --- a/p2p/dial.go +++ b/p2p/dial.go @@ -47,6 +47,19 @@ const ( maxResolveDelay = time.Hour ) +type NodeDialer interface { + Dial(*discover.Node) (net.Conn, error) +} + +type TCPDialer struct { + *net.Dialer +} + +func (t TCPDialer) Dial(dest *discover.Node) (net.Conn, error) { + addr := &net.TCPAddr{IP: dest.IP, Port: int(dest.TCP)} + return t.Dialer.Dial("tcp", addr.String()) +} + // dialstate schedules dials and discovery lookups. // it get's a chance to compute new tasks on every iteration // of the main loop in server.run. @@ -318,14 +331,13 @@ func (t *dialTask) resolve(srv *server) bool { // dial performs the actual connection attempt. func (t *dialTask) dial(srv *server, dest *discover.Node) bool { - addr := &net.TCPAddr{IP: dest.IP, Port: int(dest.TCP)} - fd, err := srv.Dialer.Dial("tcp", addr.String()) + fd, err := srv.Dialer.Dial(dest) if err != nil { log.Trace("Dial error", "task", t, "err", err) return false } mfd := newMeteredConn(fd, false) - srv.setupConn(mfd, t.flags, dest) + srv.SetupConn(mfd, t.flags, dest) return true } diff --git a/p2p/dial_test.go b/p2p/dial_test.go index 55c0154f80..ad18ef9abe 100644 --- a/p2p/dial_test.go +++ b/p2p/dial_test.go @@ -597,8 +597,8 @@ func TestDialResolve(t *testing.T) { } // Now run the task, it should resolve the ID once. - config := Config{Dialer: &net.Dialer{Deadline: time.Now().Add(-5 * time.Minute)}} - srv := &server{ntab: table, Config: config} + config := Config{Dialer: TCPDialer{&net.Dialer{Deadline: time.Now().Add(-5 * time.Minute)}}} + srv := &Server{ntab: table, Config: config} tasks[0].Do(srv) if !reflect.DeepEqual(table.resolveCalls, []discover.NodeID{dest.ID}) { t.Fatalf("wrong resolve calls, got %v", table.resolveCalls) diff --git a/p2p/protocols/protocol.go b/p2p/protocols/protocol.go index 12fce6b93d..bbe4198455 100644 --- a/p2p/protocols/protocol.go +++ b/p2p/protocols/protocol.go @@ -30,13 +30,13 @@ Standard protocol supports: package protocols import ( + "context" "fmt" "reflect" - "time" + "sync" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/p2p" - "github.com/ethereum/go-ethereum/p2p/discover" ) // error codes used by this protocol scheme @@ -109,155 +109,104 @@ func errorf(code int, format string, params ...interface{}) *Error { return self } -// implements the code table spec -// listing the message codes and types etc -// and further metadata about the protocol -type CodeMap struct { - Name string // name of the protocol - Version uint // version - MaxMsgSize int // max length of message payload size - codepos int // the subsequent code - codes map[uint64]reflect.Type // index of codes to msg types - to create zero values - messages map[reflect.Type]uint64 // index of types to codes, for sending by type +// Spec is a protocol specification including its name and version as well as +// the types of messages which are exchanged +type Spec struct { + // Name is the name of the protocol, often a three-letter word + Name string + + // Version is the version number of the protocol + Version uint + + // MaxMsgSize is the maximum accepted length of the message payload + MaxMsgSize uint32 + + // Messages is a list of message types which this protocol uses, with + // each message type being sent with its array index as the code (so + // [&foo{}, &bar{}, &baz{}] would send foo, bar and baz with codes + // 0, 1 and 2 respectively) + Messages []interface{} + + initOnce sync.Once + codes map[reflect.Type]uint64 + types map[uint64]reflect.Type } -func (self *CodeMap) GetInterface(code uint64) (interface{}, bool) { - typ, found := self.codes[code] - if !found { +func (s *Spec) init() { + s.initOnce.Do(func() { + s.codes = make(map[reflect.Type]uint64, len(s.Messages)) + s.types = make(map[uint64]reflect.Type, len(s.Messages)) + for i, msg := range s.Messages { + code := uint64(i) + typ := reflect.TypeOf(msg) + if typ.Kind() == reflect.Ptr { + typ = typ.Elem() + } + s.codes[typ] = code + s.types[code] = typ + } + }) +} + +func (s *Spec) Length() uint64 { + return uint64(len(s.Messages)) +} + +func (s *Spec) GetCode(msg interface{}) (uint64, bool) { + s.init() + typ := reflect.TypeOf(msg) + if typ.Kind() == reflect.Ptr { + typ = typ.Elem() + } + code, ok := s.codes[typ] + return code, ok +} + +func (s *Spec) NewMsg(code uint64) (interface{}, bool) { + s.init() + typ, ok := s.types[code] + if !ok { return nil, false } - val := reflect.New(typ) - return val.Interface(), true -} - -func (self *CodeMap) GetCode(msg interface{}) (uint64, bool) { - code, found := self.messages[reflect.TypeOf(msg)] - return code, found -} - -// NewCodeMap construct the code to type map for the protocol -func NewCodeMap(name string, version uint, maxMsgSize int) *CodeMap { - return &CodeMap{ - Name: name, - Version: version, - MaxMsgSize: maxMsgSize, - messages: make(map[reflect.Type]uint64), - codes: make(map[uint64]reflect.Type), - } -} - -// Length returns the current highes codepos + 1 -func (self *CodeMap) Length() uint64 { - return uint64(self.codepos) -} - -// Register defines a new series of codes starting on series, incrementing -func (self *CodeMap) Register(series int, msgs ...interface{}) { - self.codepos = series - for _, msg := range msgs { - typ := reflect.TypeOf(msg) - _, found := self.messages[typ] - if found { - // ignore duplicates - continue - } - // next code assigned to message type typ - self.messages[typ] = uint64(self.codepos) - self.codes[uint64(self.codepos)] = typ - self.codepos++ - } -} - -func NewProtocol(protocolname string, protocolversion uint, run func(*Peer) error, ct *CodeMap, peerInfo func(id discover.NodeID) interface{}, nodeInfo func() interface{}) *p2p.Protocol { - - // PeerInfo is an optional helper method to retrieve protocol specific metadata - // about a certain peer in the network. If an info retrieval function is set, - // but returns nil, it is assumed that the protocol handshake is still running. - r := func(p *p2p.Peer, rw p2p.MsgReadWriter) error { - return run(NewPeer(p, ct, rw)) - - } - - return &p2p.Protocol{ - Name: protocolname, - Version: protocolversion, - Length: ct.Length(), - Run: r, - PeerInfo: peerInfo, - NodeInfo: nodeInfo, - } -} - -type Disconnect struct { - err error + return reflect.New(typ).Interface(), true } // A Peer represents a remote peer or protocol instance that is running on a peer connection with // a remote peer type Peer struct { - ct *CodeMap // CodeMap for the protocol - *p2p.Peer // the p2p.Peer object representing the remote - rw p2p.MsgReadWriter // p2p.MsgReadWriter to send messages to and read messages from - handlers map[reflect.Type][]func(interface{}) error // message type -> message handler callback(s) map + *p2p.Peer // the p2p.Peer object representing the remote + rw p2p.MsgReadWriter // p2p.MsgReadWriter to send messages to and read messages from + spec *Spec Errc chan error - ready chan bool // blocking send until handshake finishes + wErrc chan error // write error channel } // NewPeer returns a new peer // this constructor is called by the p2p.Protocol#Run function // the first two arguments are comming the arguments passed to p2p.Protocol.Run function // the third argument is the CodeMap describing the protocol messages and options -func NewPeer(p *p2p.Peer, ct *CodeMap, rw p2p.MsgReadWriter) *Peer { - ready := make(chan bool) - defer close(ready) +func NewPeer(p *p2p.Peer, rw p2p.MsgReadWriter, spec *Spec) *Peer { return &Peer{ - ct: ct, - Peer: p, - rw: rw, - Errc: make(chan error), - ready: ready, - handlers: make(map[reflect.Type][]func(interface{}) error), + Peer: p, + rw: rw, + spec: spec, + Errc: make(chan error), + wErrc: make(chan error), } } -// Register is called on the peer typically within the constructor of service instances running on peer connections -// These constructors are called by the p2p.Protocol#Run function -// It ties handler callbackss for specific message types -// A message type can have several handlers registered by the same or different protocol services -// Register is meant to be called once, deregistering is not currently supported therefore -// handlers are assumed to be static across handshake renegotiations -// i.e., a service instance either handles a message or not (irrespective of the handshake) -// it panics if the message type is not defined in the CodeMap -func (self *Peer) Register(msg interface{}, handler func(interface{}) error) uint64 { - typ := reflect.TypeOf(msg) - code, found := self.ct.messages[typ] - if !found { - panic(fmt.Sprintf("message type '%v' unknown ", typ)) - } - log.Trace(fmt.Sprintf("register handle for %v", typ)) - self.handlers[typ] = append(self.handlers[typ], handler) - return code -} - // Run starts the forever loop that handles incoming messages // called within the p2p.Protocol#Run function -func (self *Peer) Run() error { +func (self *Peer) Run(handler func(msg interface{}) error) error { go func() { for { - _, err := self.handleIncoming() - if err != nil { + if err := self.handleIncoming(handler); err != nil { self.Errc <- err return } } }() - err := <-self.Errc - d := &Disconnect{err} - for _, f := range self.handlers[reflect.TypeOf(d)] { - log.Trace(fmt.Sprintf("disconnect hook for %v", d)) - f(err) - } - return err + return <-self.Errc } // Drop disconnects a peer. @@ -275,26 +224,12 @@ func (self *Peer) Drop(err error) { // this low level call will be wrapped by libraries providing routed or broadcast sends // but often just used to forward and push messages to directly connected peers func (self *Peer) Send(msg interface{}) error { - <-self.ready - return self.send(msg) -} - -func (self *Peer) send(msg interface{}) error { - code, found := self.ct.GetCode(msg) + code, found := self.spec.GetCode(msg) if !found { return errorf(ErrInvalidMsgType, "%v", code) } log.Trace(fmt.Sprintf("=> msg #%d TO %v : %v", code, self.ID(), msg)) - - return p2p.Send(self.rw, uint64(code), msg) -} - -func (self *Peer) DisconnectHook(f func(error)) { - typ := reflect.TypeOf(&Disconnect{}) - self.handlers[typ] = append(self.handlers[typ], func(e interface{}) error { - f(e.(error)) - return nil - }) + return p2p.Send(self.rw, code, msg) } // handleIncoming(code) @@ -302,85 +237,72 @@ func (self *Peer) DisconnectHook(f func(error)) { // if this returns an error the loop returns and the peer is disconnected with the error // checks message size, out-of-range message codes, handles decoding with reflection, // call handlers as callback onside -func (self *Peer) handleIncoming() (interface{}, error) { +func (self *Peer) handleIncoming(handle func(msg interface{}) error) error { msg, err := self.rw.ReadMsg() if err != nil { - return nil, err + return err } log.Trace(fmt.Sprintf("<= %v", msg)) // make sure that the payload has been fully consumed defer msg.Discard() - if msg.Size > uint32(self.ct.MaxMsgSize) { - return nil, errorf(ErrMsgTooLong, "%v > %v", msg.Size, self.ct.MaxMsgSize) + if msg.Size > self.spec.MaxMsgSize { + return errorf(ErrMsgTooLong, "%v > %v", msg.Size, self.spec.MaxMsgSize) } - // check if the message code is correct - maxMsgCode := uint(len(self.ct.messages)) - if msg.Code >= uint64(maxMsgCode) { - return nil, errorf(ErrInvalidMsgCode, "%v (>=%v)", msg.Code, maxMsgCode) + val, ok := self.spec.NewMsg(msg.Code) + if !ok { + return errorf(ErrInvalidMsgCode, "%v", msg.Code) } - - // it is safe to be unsafe here - typ := self.ct.codes[msg.Code] - val := reflect.New(typ) - req := val.Elem() - req.Set(reflect.Zero(typ)) - if err := msg.Decode(val.Interface()); err != nil { - return nil, errorf(ErrDecode, "<= %v: %v", msg, err) + if err := msg.Decode(val); err != nil { + return errorf(ErrDecode, "<= %v: %v", msg, err) } - log.Trace(fmt.Sprintf("<= %v FROM %v %v %v", msg, self.ID(), req, typ)) + log.Trace(fmt.Sprintf("<= %v FROM %v %T %v", msg, self.ID(), val, val)) // call the registered handler callbacks // a registered callback take the decoded message as argument as an interface // which the handler is supposed to cast to the appropriate type // it is entirely safe not to check the cast in the handler since the handler is // chosen based on the proper type in the first place - handlers := self.handlers[typ] - if len(handlers) == 0 { - log.Trace(fmt.Sprintf("no handler (msg code %v)", msg.Code)) - // return nil, errorf(ErrNoHandler, "(msg code %v)", msg.Code) - } else { - for i, f := range handlers { - log.Trace(fmt.Sprintf("handler %v for %v", i, typ)) - err = f(req.Interface()) - if err != nil { - return nil, errorf(ErrHandler, "(msg code %v): %v", msg.Code, err) - } - } + if err := handle(val); err != nil { + return errorf(ErrHandler, "(msg code %v): %v", msg.Code, err) } - return req.Interface(), nil + return nil } // Handshake initiates a handshake on the peer connection // * the argument is the local handshake to be sent to the remote peer // * expects a remote handshake back of the same type // returns the remote hs and an error -func (self *Peer) Handshake(hs interface{}, handshakeTimeout time.Duration) (rhs interface{}, err error) { - typ := reflect.TypeOf(hs) - _, found := self.ct.messages[typ] - if !found { - return nil, errorf(ErrHandshake, "unknown handshake message type: %v", typ) +func (self *Peer) Handshake(ctx context.Context, hs interface{}) (interface{}, error) { + if _, ok := self.spec.GetCode(hs); !ok { + return nil, errorf(ErrHandshake, "unknown handshake message type: %T", hs) } - self.ready = make(chan bool) - received := make(chan bool) - defer close(self.ready) + errc := make(chan error, 2) go func() { - defer close(received) - // receiving and validating remote handshake, expect code - rhs, err = self.handleIncoming() - if err != nil { - err = errorf(ErrHandshake, "'%v': %v", self.ct.Name, err) + if err := self.Send(hs); err != nil { + errc <- errorf(ErrHandshake, "cannot send: %v", err) } }() - if e := self.send(hs); e != nil { - return nil, errorf(ErrHandshake, "cannot send: %v", e) - } - + hsc := make(chan interface{}) + go func() { + var rhs interface{} + err := self.handleIncoming(func(msg interface{}) error { + rhs = msg + return nil + }) + if err != nil { + errc <- err + return + } + hsc <- rhs + }() select { - case <-received: - case <-time.NewTimer(handshakeTimeout).C: - err = errorf(ErrHandshake, "timeout after %v", handshakeTimeout) + case rhs := <-hsc: + return rhs, nil + case <-ctx.Done(): + return nil, ctx.Err() + case err := <-errc: + return nil, err } - return rhs, err } diff --git a/p2p/protocols/protocol_test.go b/p2p/protocols/protocol_test.go index fc1238c5f7..e4f7a145a1 100644 --- a/p2p/protocols/protocol_test.go +++ b/p2p/protocols/protocol_test.go @@ -1,6 +1,8 @@ package protocols import ( + "context" + "errors" "fmt" "os" "testing" @@ -13,7 +15,7 @@ import ( ) func init() { - log.Root().SetHandler(log.LvlFilterHandler(log.LvlError, log.StreamHandler(os.Stderr, log.TerminalFormat(false)))) + log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(false)))) } // handshake message type @@ -55,28 +57,26 @@ const networkId = "420" // newProtocol sets up a protocol // the run function here demonstrates a typical protocol using peerPool, handshake // and messages registered to handlers -func newProtocol(pp *p2ptest.TestPeerPool) adapters.RunProtocol { - ct := NewCodeMap("test", 42, 1024) - ct.Register(0, &protoHandshake{}, &hs0{}, &kill{}, &drop{}) +func newProtocol(pp *p2ptest.TestPeerPool) func(*p2p.Peer, p2p.MsgReadWriter) error { + spec := &Spec{ + Name: "test", + Version: 42, + MaxMsgSize: 10 * 1024, + Messages: []interface{}{ + protoHandshake{}, + hs0{}, + kill{}, + drop{}, + }, + } return func(p *p2p.Peer, rw p2p.MsgReadWriter) error { - peer := NewPeer(p, ct, rw) - - // demonstrates use of peerPool, killing another peer connection as a response to a message - peer.Register(&kill{}, func(msg interface{}) error { - id := msg.(*kill).C - pp.Get(id).Drop(fmt.Errorf("killed")) - log.Trace(fmt.Sprintf("id %v killed", id)) - return nil - }) - - // for testing we can trigger self induced disconnect upon receiving drop message - peer.Register(&drop{}, func(msg interface{}) error { - return fmt.Errorf("dropped") - }) + peer := NewPeer(p, rw, spec) // initiate one-off protohandshake and check validity - phs := &protoHandshake{ct.Version, networkId} - hs, err := peer.Handshake(phs, time.Second) + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + phs := &protoHandshake{42, networkId} + hs, err := peer.Handshake(ctx, phs) if err != nil { return err } @@ -88,7 +88,7 @@ func newProtocol(pp *p2ptest.TestPeerPool) adapters.RunProtocol { lhs := &hs0{42} // module handshake demonstrating a simple repeatable exchange of same-type message - hs, err = peer.Handshake(lhs, time.Second) + hs, err = peer.Handshake(ctx, lhs) if err != nil { return err } @@ -97,19 +97,40 @@ func newProtocol(pp *p2ptest.TestPeerPool) adapters.RunProtocol { return fmt.Errorf("handshake mismatch remote %v > local %v", rmhs.C, lhs.C) } - peer.Register(lhs, func(msg interface{}) error { - rhs := msg.(*hs0) - if rhs.C > lhs.C { - return fmt.Errorf("handshake mismatch remote %v > local %v", rhs.C, lhs.C) + handle := func(msg interface{}) error { + switch msg := msg.(type) { + + case *protoHandshake: + return errors.New("duplicate handshake") + + case *hs0: + rhs := msg + if rhs.C > lhs.C { + return fmt.Errorf("handshake mismatch remote %v > local %v", rhs.C, lhs.C) + } + lhs.C += rhs.C + return peer.Send(lhs) + + case *kill: + // demonstrates use of peerPool, killing another peer connection as a response to a message + id := msg.C + pp.Get(id).Drop(errors.New("killed")) + log.Trace(fmt.Sprintf("id %v killed", id)) + return nil + + case *drop: + // for testing we can trigger self induced disconnect upon receiving drop message + return errors.New("dropped") + + default: + return fmt.Errorf("unknown message type: %T", msg) } - lhs.C += rhs.C - return peer.Send(lhs) - }) + } log.Trace(fmt.Sprintf("adding peer %v", peer)) pp.Add(peer) defer pp.Remove(peer) - err = peer.Run() + err = peer.Run(handle) log.Trace(fmt.Sprintf("peer %v protocol quitting: %v", peer, err)) return err diff --git a/p2p/server.go b/p2p/server.go index c641e3a628..14a987d875 100644 --- a/p2p/server.go +++ b/p2p/server.go @@ -131,7 +131,7 @@ type Config struct { // If Dialer is set to a non-nil value, the given Dialer // is used to dial outbound peer connections. - Dialer *net.Dialer `toml:"-"` + Dialer NodeDialer `toml:"-"` // If NoDial is true, the server will not dial any peers. NoDial bool `toml:",omitempty"` @@ -144,6 +144,7 @@ type Config struct { type Server interface { Start() error Stop() error + SetupConn(net.Conn, connFlag, *discover.Node) AddPeer(node *discover.Node) RemovePeer(node *discover.Node) SubscribeEvents(ch chan *PeerEvent) event.Subscription @@ -385,7 +386,7 @@ func (srv *server) Start() (err error) { srv.newTransport = newRLPX } if srv.Dialer == nil { - srv.Dialer = &net.Dialer{Timeout: defaultDialTimeout} + srv.Dialer = TCPDialer{&net.Dialer{Timeout: defaultDialTimeout}} } srv.quit = make(chan struct{}) srv.addpeer = make(chan *conn) @@ -697,7 +698,7 @@ func (srv *server) listenLoop() { // Spawn the handler. It will give the slot back when the connection // has been established. go func() { - srv.setupConn(fd, inboundConn, nil) + srv.SetupConn(fd, inboundConn, nil) slots <- struct{}{} }() } @@ -706,7 +707,7 @@ func (srv *server) listenLoop() { // setupConn runs the handshakes and attempts to add the connection // as a peer. It returns when the connection has been added as a peer // or the handshakes have failed. -func (srv *server) setupConn(fd net.Conn, flags connFlag, dialDest *discover.Node) { +func (srv *server) SetupConn(fd net.Conn, flags connFlag, dialDest *discover.Node) { // Prevent leftover pending conns from entering the handshake. srv.lock.Lock() running := srv.running diff --git a/p2p/simulations/adapters/inproc.go b/p2p/simulations/adapters/inproc.go index 6a851fda29..3226446927 100644 --- a/p2p/simulations/adapters/inproc.go +++ b/p2p/simulations/adapters/inproc.go @@ -17,14 +17,13 @@ package adapters import ( - "context" "errors" "fmt" + "math" "net" "sync" "github.com/ethereum/go-ethereum/event" - "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/discover" @@ -73,15 +72,28 @@ func (s *SimAdapter) NewNode(config *NodeConfig) (Node, error) { node := &SimNode{ Id: id, + config: config, adapter: s, serviceFunc: serviceFunc, - peers: make(map[discover.NodeID]MsgReadWriteCloser), - dropPeers: make(chan struct{}), } s.nodes[id.NodeID] = node return node, nil } +func (s *SimAdapter) Dial(dest *discover.Node) (conn net.Conn, err error) { + node, ok := s.GetNode(dest.ID) + if !ok { + return nil, fmt.Errorf("unknown node: %s", dest.ID) + } + srv := node.Server() + if srv == nil { + return nil, fmt.Errorf("node not running: %s", dest.ID) + } + pipe1, pipe2 := net.Pipe() + go srv.SetupConn(pipe1, 0, nil) + return pipe2, nil +} + // GetNode returns the node with the given ID if it exists func (s *SimAdapter) GetNode(id discover.NodeID) (*SimNode, bool) { s.mtx.RLock() @@ -90,14 +102,6 @@ func (s *SimAdapter) GetNode(id discover.NodeID) (*SimNode, bool) { return node, ok } -// MsgReadWriteCloser wraps a MsgReadWriter with the addition of a Close method -// so we can simulate the closing of a p2p connection (which usually happens by -/// closing the underlying TCP connection) -type MsgReadWriteCloser interface { - p2p.MsgReadWriter - Close() error -} - // SimNode is an in-memory node which connects to other SimNodes using an // in-memory p2p.MsgReadWriter pipe, running an underlying service protocol // directly over that pipe. @@ -107,17 +111,13 @@ type MsgReadWriteCloser interface { type SimNode struct { lock sync.RWMutex Id *NodeId + config *NodeConfig adapter *SimAdapter - running node.Service serviceFunc ServiceFunc - peers map[discover.NodeID]MsgReadWriteCloser - peerFeed event.Feed + node *node.Node + running node.Service client *rpc.Client rpcMux *rpcMux - - // dropPeers is used to force peer disconnects when - // the node is stopped - dropPeers chan struct{} } // Addr returns the node's discovery address @@ -127,7 +127,7 @@ func (self *SimNode) Addr() []byte { // Node returns a discover.Node representing the SimNode func (self *SimNode) Node() *discover.Node { - return discover.NewNode(self.Id.NodeID, nil, 0, 0) + return discover.NewNode(self.Id.NodeID, net.IP{127, 0, 0, 1}, 30303, 30303) } // Client returns an rpc.Client which can be used to communicate with the @@ -154,83 +154,57 @@ func (self *SimNode) ServeRPC(conn net.Conn) error { return nil } -// Start initializes the service, starts the RPC handler and then starts -// the service -func (self *SimNode) Start(snapshot []byte) error { - service := self.serviceFunc(self.Id, snapshot) +// Snapshot creates a snapshot of the service state by calling the +// simulation_snapshot RPC method +func (self *SimNode) Snapshot() ([]byte, error) { + self.lock.Lock() + defer self.lock.Unlock() + if self.client == nil { + return nil, errors.New("RPC not started") + } + var snapshot []byte + return snapshot, self.client.Call(&snapshot, "simulation_snapshot") +} - // for simplicity, only support single protocol services (simulating - // multiple protocols on the same peer is extra effort, and we don't - // currently run any simulations which run multiple protocols) - if len(service.Protocols()) != 1 { - return errors.New("service must have a single protocol") +// Start starts the RPC handler and the underlying service +func (self *SimNode) Start(snapshot []byte) error { + self.lock.Lock() + defer self.lock.Unlock() + if self.node != nil { + return errors.New("node already started") } - self.dropPeers = make(chan struct{}) - if err := self.startRPC(service); err != nil { + newService := func(ctx *node.ServiceContext) (node.Service, error) { + service := self.serviceFunc(self.Id, snapshot) + self.running = service + return service, nil + } + + node, err := node.New(&node.Config{ + P2P: p2p.Config{ + PrivateKey: self.config.PrivateKey, + MaxPeers: math.MaxInt32, + NoDiscovery: true, + Dialer: self.adapter, + EnableMsgEvents: true, + }, + NoUSB: true, + }) + if err != nil { return err } - self.running = service - return service.Start(&simServer{self}) -} -// simServer wraps a SimNode but modifies the Start method signature so that -// it implements the p2p.Server interface (the Start method is never actually -// called when using the SimAdapter) -type simServer struct { - *SimNode -} - -func (s *simServer) Start() error { - return nil -} - -// Stop stops the RPC handler, stops the underlying service and disconnects -// any currently connected peers -func (self *SimNode) Stop() error { - self.stopRPC() - close(self.dropPeers) - return self.running.Stop() -} - -// Running returns whether or not the service is running -func (self *SimNode) Running() bool { - self.lock.Lock() - defer self.lock.Unlock() - return self.running != nil -} - -// Service returns the running node.Service -func (self *SimNode) Service() node.Service { - return self.running -} - -// startRPC starts an RPC server and connects to it using an in-process RPC -// client -func (self *SimNode) startRPC(service node.Service) error { - self.lock.Lock() - defer self.lock.Unlock() - if self.client != nil { - return nil - // return errors.New("RPC already started") + if err := node.Register(newService); err != nil { + return err } - // add SimAdminAPI so that the network can call the - // AddPeer, RemovePeer and PeerEvents RPC methods - apis := append(service.APIs(), []rpc.API{ - { - Namespace: "admin", - Version: "1.0", - Service: &SimAdminAPI{self}, - }, - }...) + if err := node.Start(); err != nil { + return err + } - // start the RPC handler - handler := rpc.NewServer() - for _, api := range apis { - if err := handler.RegisterName(api.Namespace, api.Service); err != nil { - return fmt.Errorf("error registering RPC: %s", err) - } + handler, err := node.RPCHandler() + if err != nil { + return err } // create an in-process RPC multiplexer @@ -241,197 +215,55 @@ func (self *SimNode) startRPC(service node.Service) error { // create an in-process RPC client self.client = self.rpcMux.Client() + self.node = node + return nil } -// stopRPC closes the node's RPC client -func (self *SimNode) stopRPC() { +func (self *SimNode) Stop() error { self.lock.Lock() defer self.lock.Unlock() - if self.client != nil { - self.client.Close() - self.client = nil - self.rpcMux = nil + if self.node == nil { + return nil } + if err := self.node.Stop(); err != nil { + return err + } + self.node = nil + return nil } -// RemovePeer removes the given node as a peer by looking up the corresponding -// p2p.MsgReadWriter pipe and closing it (which will cause both the local -// and peer Protocol.Run functions to exit) -func (self *SimNode) RemovePeer(peer *discover.Node) { +// Service returns the underlying node.Service +func (self *SimNode) Service() node.Service { self.lock.Lock() defer self.lock.Unlock() - peerRW, exists := self.peers[peer.ID] - if !exists { - return - } - peerRW.Close() - delete(self.peers, peer.ID) - log.Trace(fmt.Sprintf("dropped peer %v", peer.ID)) + return self.running } -// AddPeer adds the given node as a peer by creating a p2p.MsgReadWriter pipe -// and running both the local and peer's Protocol.Run function over the pipe -func (self *SimNode) AddPeer(peer *discover.Node) { +func (self *SimNode) Server() *p2p.Server { self.lock.Lock() defer self.lock.Unlock() - if _, exists := self.peers[peer.ID]; exists { - return + if self.node == nil { + return nil } - peerNode, exists := self.adapter.GetNode(peer.ID) - if !exists { - panic(fmt.Sprintf("unknown peer: %s", peer.ID)) - } - if !peerNode.Running() { - return - } - p1, p2 := p2p.MsgPipe() - localRW := p2p.NewMsgEventer(p1, &self.peerFeed, peer.ID) - peerRW := p2p.NewMsgEventer(p2, &self.peerFeed, self.Id.NodeID) - self.peers[peer.ID] = peerRW - peerNode.RunProtocol(self, peerRW) - self.RunProtocol(peerNode, localRW) + return self.node.Server() } -// SubscribeEvents subscribes the given channel to p2p peer events func (self *SimNode) SubscribeEvents(ch chan *p2p.PeerEvent) event.Subscription { - return self.peerFeed.Subscribe(ch) + srv := self.Server() + if srv == nil { + panic("node not running") + } + return srv.SubscribeEvents(ch) } -// PeerCount returns the number of currently connected peers -func (self *SimNode) PeerCount() int { - self.lock.Lock() - defer self.lock.Unlock() - return len(self.peers) -} - -// NodeInfo returns information about the node func (self *SimNode) NodeInfo() *p2p.NodeInfo { - self.lock.Lock() - defer self.lock.Unlock() - info := &p2p.NodeInfo{ - ID: self.Id.String(), - Enode: self.Node().String(), - Protocols: make(map[string]interface{}), - } - if self.running != nil { - for _, proto := range self.running.Protocols() { - nodeInfo := interface{}("unknown") - if query := proto.NodeInfo; query != nil { - nodeInfo = proto.NodeInfo() - } - info.Protocols[proto.Name] = nodeInfo + server := self.Server() + if server == nil { + return &p2p.NodeInfo{ + ID: self.Id.String(), + Enode: self.Node().String(), } } - return info -} - -// PeersInfo is a stub so that SimNode implements p2p.Server -func (self *SimNode) PeersInfo() (info []*p2p.PeerInfo) { - return nil -} - -// Snapshot creates a snapshot of the running service -func (self *SimNode) Snapshot() ([]byte, error) { - self.lock.Lock() - service := self.running - self.lock.Unlock() - if service == nil { - return nil, errors.New("service not running") - } - return SnapshotAPI{service}.Snapshot() -} - -// RunProtocol runs the underlying service's protocol with the peer using the -// given MsgReadWriteCloser, emitting peer add / drop events for peer event -// subscribers -func (self *SimNode) RunProtocol(peer *SimNode, rw MsgReadWriteCloser) { - // close the rw if the node is stopped to disconnect the peer - go func() { - <-self.dropPeers - log.Trace("dropping peer", "self.id", self.Id, "peer.id", peer.Id) - rw.Close() - }() - - id := peer.Id - log.Trace(fmt.Sprintf("protocol starting on peer %v (connection with %v)", self.Id, id)) - protocol := self.running.Protocols()[0] - p := p2p.NewPeer(id.NodeID, id.Label(), []p2p.Cap{}) - go func() { - // emit peer add event - self.peerFeed.Send(&p2p.PeerEvent{ - Type: p2p.PeerEventTypeAdd, - Peer: id.NodeID, - }) - - // run the protocol - err := protocol.Run(p, rw) - - // remove the peer - self.RemovePeer(peer.Node()) - log.Trace(fmt.Sprintf("protocol quit on peer %v (connection with %v broken: %v)", self.Id, id, err)) - - // emit peer drop event - self.peerFeed.Send(&p2p.PeerEvent{ - Type: p2p.PeerEventTypeDrop, - Peer: id.NodeID, - Error: err.Error(), - }) - }() -} - -// SimAdminAPI implements the AddPeer and RemovePeer RPC methods (API -// compatible with node.PrivateAdminAPI) -type SimAdminAPI struct { - *SimNode -} - -func (api *SimAdminAPI) AddPeer(url string) (bool, error) { - node, err := discover.ParseNode(url) - if err != nil { - return false, fmt.Errorf("invalid enode: %v", err) - } - api.SimNode.AddPeer(node) - return true, nil -} - -func (api *SimAdminAPI) RemovePeer(url string) (bool, error) { - node, err := discover.ParseNode(url) - if err != nil { - return false, fmt.Errorf("invalid enode: %v", err) - } - api.SimNode.RemovePeer(node) - return true, nil -} - -// PeerEvents creates an RPC subscription which receives peer events from the -// underlying p2p.Server -func (api *SimAdminAPI) PeerEvents(ctx context.Context) (*rpc.Subscription, error) { - notifier, supported := rpc.NotifierFromContext(ctx) - if !supported { - return &rpc.Subscription{}, rpc.ErrNotificationsUnsupported - } - - rpcSub := notifier.CreateSubscription() - - go func() { - events := make(chan *p2p.PeerEvent) - sub := api.SubscribeEvents(events) - defer sub.Unsubscribe() - - for { - select { - case event := <-events: - notifier.Notify(rpcSub.ID, event) - case <-sub.Err(): - return - case <-rpcSub.Err(): - return - case <-notifier.Closed(): - return - } - } - }() - - return rpcSub, nil + return server.NodeInfo() } diff --git a/p2p/testing/protocolsession.go b/p2p/testing/protocolsession.go index 1dfbb6ab49..d611cd45a5 100644 --- a/p2p/testing/protocolsession.go +++ b/p2p/testing/protocolsession.go @@ -13,7 +13,7 @@ import ( ) type ProtocolSession struct { - *adapters.SimNode + p2p.Server Ids []*adapters.NodeId adapter *adapters.SimAdapter diff --git a/p2p/testing/protocoltester.go b/p2p/testing/protocoltester.go index 16a23f120f..695cfd0292 100644 --- a/p2p/testing/protocoltester.go +++ b/p2p/testing/protocoltester.go @@ -20,10 +20,10 @@ type ProtocolTester struct { func NewProtocolTester(t *testing.T, id *adapters.NodeId, n int, run func(*p2p.Peer, p2p.MsgReadWriter) error) *ProtocolTester { services := map[string]adapters.ServiceFunc{ - "test": func(id *adapters.NodeId) node.Service { + "test": func(id *adapters.NodeId, _ []byte) node.Service { return &testNode{run} }, - "mock": func(id *adapters.NodeId) node.Service { + "mock": func(id *adapters.NodeId, _ []byte) node.Service { return newMockNode() }, } @@ -47,7 +47,7 @@ func NewProtocolTester(t *testing.T, id *adapters.NodeId, n int, run func(*p2p.P events := make(chan *p2p.PeerEvent, 1000) node.SubscribeEvents(events) ps := &ProtocolSession{ - SimNode: node, + Server: node.Server(), Ids: peerIDs, adapter: adapter, events: events, @@ -86,7 +86,10 @@ type testNode struct { } func (t *testNode) Protocols() []p2p.Protocol { - return []p2p.Protocol{{Run: t.run}} + return []p2p.Protocol{{ + Length: 100, + Run: t.run, + }} } func (t *testNode) APIs() []rpc.API { diff --git a/swarm/network/discovery.go b/swarm/network/discovery.go index 55a75ed858..b1830118d7 100644 --- a/swarm/network/discovery.go +++ b/swarm/network/discovery.go @@ -8,13 +8,6 @@ import ( // discovery bzz overlay extension doing peer relaying -// messages related to peer discovery -var DiscoveryMsgs = []interface{}{ - &getPeersMsg{}, - &peersMsg{}, - &subPeersMsg{}, -} - type discPeer struct { *bzzPeer overlay Overlay @@ -32,14 +25,26 @@ func NewDiscovery(p *bzzPeer, o Overlay) *discPeer { peers: make(map[string]bool), } self.seen(self) - - p.Register(&peersMsg{}, self.handlePeersMsg) - p.Register(&getPeersMsg{}, self.handleGetPeersMsg) - p.Register(&subPeersMsg{}, self.handleSubPeersMsg) - return self } +func (self *discPeer) HandleMsg(msg interface{}) error { + switch msg := msg.(type) { + + case *peersMsg: + return self.handlePeersMsg(msg) + + case *getPeersMsg: + return self.handleGetPeersMsg(msg) + + case *subPeersMsg: + return self.handleSubPeersMsg(msg) + + default: + return fmt.Errorf("unknown message type: %T", msg) + } +} + // NotifyPeer notifies the receiver remote end of a peer p or PO po. // callback for overlay driver func (self *discPeer) NotifyPeer(p OverlayPeer, po uint8) error { @@ -109,9 +114,8 @@ func (self subPeersMsg) String() string { return fmt.Sprintf("%T: request peers > PO%02d. ", self, self.ProxLimit) } -func (self *discPeer) handleSubPeersMsg(msg interface{}) error { - spm := msg.(*subPeersMsg) - self.proxLimit = spm.ProxLimit +func (self *discPeer) handleSubPeersMsg(msg *subPeersMsg) error { + self.proxLimit = msg.ProxLimit if !self.sentPeers { var peers []*bzzAddr self.overlay.EachConn(self.Over(), 255, func(p OverlayConn, po int, isproxbin bool) bool { @@ -138,17 +142,16 @@ func (self *discPeer) handleSubPeersMsg(msg interface{}) error { // handlePeersMsg called by the protocol when receiving peerset (for target address) // list of nodes ([]PeerAddr in peersMsg) is added to the overlay db using the // Register interface method -func (self *discPeer) handlePeersMsg(msg interface{}) error { +func (self *discPeer) handlePeersMsg(msg *peersMsg) error { // register all addresses - as := msg.(*peersMsg).Peers - if len(as) == 0 { + if len(msg.Peers) == 0 { log.Debug(fmt.Sprintf("whoops, no peers in incoming peersMsg from %v", self)) return nil } var c chan OverlayAddr go func() { - for _, a := range as { + for _, a := range msg.Peers { self.seen(a) c <- a } @@ -161,18 +164,17 @@ func (self *discPeer) handlePeersMsg(msg interface{}) error { // peers suggestions are retrieved from the overlay topology driver // using the EachConn interface iterator method // peers sent are remembered throughout a session and not sent twice -func (self *discPeer) handleGetPeersMsg(msg interface{}) error { +func (self *discPeer) handleGetPeersMsg(msg *getPeersMsg) error { var peers []*bzzAddr - req := msg.(*getPeersMsg) i := 0 - self.overlay.EachConn(self.Over(), int(req.Order), func(p OverlayConn, po int, isproxbin bool) bool { + self.overlay.EachConn(self.Over(), int(msg.Order), func(p OverlayConn, po int, isproxbin bool) bool { i++ // only send peers we have not sent before in this session a := ToAddr(p) if self.seen(a) { peers = append(peers, a) } - return len(peers) < int(req.Max) + return len(peers) < int(msg.Max) }) if len(peers) == 0 { log.Debug(fmt.Sprintf("no peers found for %v", self)) diff --git a/swarm/network/discovery_test.go b/swarm/network/discovery_test.go index 22a61a04ea..dfab1eef5a 100644 --- a/swarm/network/discovery_test.go +++ b/swarm/network/discovery_test.go @@ -16,22 +16,18 @@ import ( func TestDiscovery(t *testing.T) { addr := RandomAddr() to := NewKademlia(addr.OAddr, NewKadParams()) - ct := BzzCodeMap(DiscoveryMsgs...) - services := func(p *bzzPeer) error { + run := func(p *bzzPeer) error { dp := NewDiscovery(p, to) - to.On(dp) + to.On(p) + defer to.Off(p) log.Trace(fmt.Sprintf("kademlia on %v", p)) - p.DisconnectHook(func(err error) { - to.Off(p) - }) - return nil + return p.Run(dp.HandleMsg) } - s := newBzzBaseTester(t, 1, addr, ct, services) + s := newBzzBaseTester(t, 1, addr, DiscoveryProtocol, run) defer s.Stop() - s.runHandshakes() s.TestExchanges(p2ptest.Exchange{ Label: "outgoing SubPeersMsg", Expects: []p2ptest.Expect{ diff --git a/swarm/network/hive.go b/swarm/network/hive.go index 2ee8cd3b46..07c1bef6b3 100644 --- a/swarm/network/hive.go +++ b/swarm/network/hive.go @@ -34,7 +34,7 @@ it uses an Overlay Topology driver (e.g., generic kademlia nodetable) to find best peer list for any target this is used by the netstore to search for content in the swarm -It handles the bzz protocol getPeersMsg peersMsg exchange +It handles the hive protocol getPeersMsg peersMsg exchange and relay the peer request process to the Overlay module peer connections and disconnections are reported and registered @@ -57,23 +57,19 @@ type Overlay interface { BaseAddr() []byte } -// ReadWriter interface to persist known peers, uses disk for real nodes -type ReadWriter interface { - ReadAll(string) ([]byte, error) - WriteAll(string, []byte) error -} - // Hive implements the PeerPool interface type Hive struct { - *HiveParams // settings - Overlay // the overlay topology driver - RW ReadWriter // ReadWriter + *HiveParams // settings + Overlay // the overlay topology driver + store Store // bookkeeping lock sync.Mutex quit chan bool toggle chan bool more chan bool + + newTicker func() hiveTicker } // HiveParams holds the config options to hive @@ -81,7 +77,7 @@ type HiveParams struct { Discovery bool // if want discovery of not PeersBroadcastSetSize uint8 // how many peers to use when relaying MaxPeersPerRequest uint8 // max size for peer address batches - CallInterval uint // polling interval fir=== + KeepAliveInterval time.Duration } // NewHiveParams returns hive config with only the @@ -90,17 +86,18 @@ func NewHiveParams() *HiveParams { Discovery: true, PeersBroadcastSetSize: 2, MaxPeersPerRequest: 5, - CallInterval: 1000, + KeepAliveInterval: time.Second, } } // Hive constructor embeds both arguments // HiveParams: config parameters // Overlay: Topology Driver Interface -func NewHive(params *HiveParams, overlay Overlay) *Hive { +func NewHive(params *HiveParams, overlay Overlay, store Store) *Hive { return &Hive{ HiveParams: params, Overlay: overlay, + store: store, } } @@ -109,8 +106,8 @@ func NewHive(params *HiveParams, overlay Overlay) *Hive { // these are called on the p2p.Server which runs on the node // af() returns an arbitrary ticker channel // rw is a read writer for json configs -func (self *Hive) Start(server p2p.Server, af func() <-chan time.Time, rw ReadWriter) error { - if rw != nil { +func (self *Hive) Start(server p2p.Server) error { + if self.store != nil { if err := self.loadPeers(); err != nil { return err } @@ -120,7 +117,7 @@ func (self *Hive) Start(server p2p.Server, af func() <-chan time.Time, rw ReadWr self.quit = make(chan bool) log.Debug("hive started") // this loop is doing bootstrapping and maintains a healthy table - go self.keepAlive(af) + go self.keepAlive() go func() { // each iteration, ask kademlia about most preferred peer for more := range self.more { @@ -163,16 +160,18 @@ func (self *Hive) Start(server p2p.Server, af func() <-chan time.Time, rw ReadWr // Stop terminates the updateloop and saves the peers func (self *Hive) Stop() { - if self.RW != nil { + if self.store != nil { self.savePeers() } // closing toggle channel quits the updateloop close(self.quit) } -// default ticker, tickinterval is taken from KadParams.CallInterval -func (self *Hive) ticker() <-chan time.Time { - return time.NewTicker(time.Duration(self.CallInterval) * time.Millisecond).C +func (self *Hive) Run(peer *bzzPeer) error { + discPeer := NewDiscovery(peer, self) + self.On(discPeer) + defer self.Off(discPeer) + return peer.Run(discPeer.HandleMsg) } // Add is called at the end of a successful protocol handshake @@ -242,27 +241,48 @@ func ToAddr(pa OverlayPeer) *bzzAddr { return pa.(*bzzPeer).bzzAddr } +type hiveTicker interface { + Ch() <-chan time.Time + Stop() +} + +type timeTicker struct { + *time.Ticker +} + +func (t *timeTicker) Ch() <-chan time.Time { + return t.C +} + // keepAlive is a forever loop // in its awake state it periodically triggers connection attempts // by writing to self.more until Kademlia Table is saturated // wake state is toggled by writing to self.toggle // it goes to sleep mode if table is saturated // it restarts if the table becomes non-full again due to disconnections -func (self *Hive) keepAlive(af func() <-chan time.Time) { - log.Trace("keep alive loop started") - alarm := af() +func (self *Hive) keepAlive() { + if self.newTicker == nil { + self.newTicker = func() hiveTicker { + return &timeTicker{time.NewTicker(self.KeepAliveInterval)} + } + } + ticker := self.newTicker() + tick := ticker.Ch() for { select { - case <-alarm: + case <-tick: log.Trace("wake up: make hive alive") self.wake() case need := <-self.toggle: - if alarm == nil && need { - alarm = af() + if ticker == nil && need { + ticker = self.newTicker() + tick = ticker.Ch() } // if hive saturated, no more peers asked - if alarm != nil && !need { - alarm = nil + if ticker != nil && !need { + ticker.Stop() + ticker = nil + tick = nil } case <-self.quit: return @@ -272,8 +292,7 @@ func (self *Hive) keepAlive(af func() <-chan time.Time) { // loadPeers, savePeer implement persistence callback/ func (self *Hive) loadPeers() error { - rw := self.RW - data, err := rw.ReadAll("peers") + data, err := self.store.Load("peers") if err != nil { return err } @@ -310,7 +329,7 @@ func (self *Hive) savePeers() error { if err != nil { return fmt.Errorf("could not encode peers: %v", err) } - if err := self.RW.WriteAll("peers", data); err != nil { + if err := self.store.Save("peers", data); err != nil { return fmt.Errorf("could not save peers: %v", err) } return nil diff --git a/swarm/network/hive_test.go b/swarm/network/hive_test.go index aaa90e71a3..473ba81c0d 100644 --- a/swarm/network/hive_test.go +++ b/swarm/network/hive_test.go @@ -16,10 +16,13 @@ type testConnect struct { ticker chan time.Time } -func (self *testConnect) ping() <-chan time.Time { +func (self *testConnect) Ch() <-chan time.Time { return self.ticker } +func (self *testConnect) Stop() { +} + func (self *testConnect) connect(na string) error { self.mu.Lock() defer self.mu.Unlock() @@ -31,38 +34,10 @@ func (self *testConnect) connect(na string) error { func newHiveTester(t *testing.T, params *HiveParams) (*bzzTester, *Hive) { // setup addr := RandomAddr() // tested peers peer address - // to := NewTestOverlay(addr.Over()) // overlay topology drive - pp := NewHive(params, nil) // hive - // pp := NewHive(params, to) // hive - - ct := BzzCodeMap(DiscoveryMsgs...) // bzz protocol code map - services := func(p *bzzPeer) error { - pp.Add(p) - p.DisconnectHook(func(err error) { - pp.Remove(p) - }) - return nil - } - - return newBzzBaseTester(t, 1, addr, ct, services), pp -} - -func TestOverlayRegistration(t *testing.T) { - params := NewHiveParams() - params.Discovery = false - s, pp := newHiveTester(t, params) - defer s.Stop() - - id := s.Ids[0] - raddr := NewAddrFromNodeId(id) - - s.runHandshakes() - - // hive should have called the overlay - // if pp.Overlay.(*testOverlay).posMap[string(raddr.Over())] == nil { - // t.Fatalf("Overlay#On not called on new peer") - // } + to := NewKademlia(addr.OAddr, NewKadParams()) + pp := NewHive(params, to, nil) // hive + return newBzzBaseTester(t, 1, addr, DiscoveryProtocol, pp.Run), pp } func TestRegisterAndConnect(t *testing.T) { @@ -73,7 +48,12 @@ func TestRegisterAndConnect(t *testing.T) { id := s.Ids[0] raddr := NewAddrFromNodeId(id) - pp.Register(raddr) + ch := make(chan OverlayAddr) + go func() { + ch <- raddr + close(ch) + }() + pp.Register(ch) // start the hive and wait for the connection tc := &testConnect{ @@ -83,18 +63,17 @@ func TestRegisterAndConnect(t *testing.T) { }, ticker: make(chan time.Time), } - pp.Start(s, tc.ping, nil) + pp.newTicker = func() hiveTicker { return tc } + pp.Start(s) defer pp.Stop() tc.ticker <- time.Now() - s.runHandshakes() - // if pp.Overlay.(*testOverlay).posMap[string(raddr.Over())] == nil { // t.Fatalf("Overlay#On not called on new peer") // } // retrieve and broadcast - ord := order(raddr.Over()) + ord := raddr.Over()[0] / 32 o := 0 if ord == 0 { o = 1 diff --git a/swarm/network/kademlia_test.go b/swarm/network/kademlia_test.go index 40e1cbac97..c6bc875976 100644 --- a/swarm/network/kademlia_test.go +++ b/swarm/network/kademlia_test.go @@ -153,11 +153,14 @@ func (k *testKademlia) Off(offs ...string) *testKademlia { } func (k *testKademlia) Register(regs ...string) *testKademlia { - var ps []Addr - for _, s := range regs { - ps = append(ps, Addr(testKadPeerAddr(s))) - } - k.Kademlia.Register(ps...) + ch := make(chan OverlayAddr) + go func() { + defer close(ch) + for _, s := range regs { + ch <- testKadPeerAddr(s) + } + }() + k.Kademlia.Register(ch) return k } diff --git a/swarm/network/protocol.go b/swarm/network/protocol.go index 523031db17..e4ad09f705 100644 --- a/swarm/network/protocol.go +++ b/swarm/network/protocol.go @@ -17,7 +17,10 @@ package network import ( + "context" + "errors" "fmt" + "sync" "time" "github.com/ethereum/go-ethereum/crypto" @@ -26,15 +29,43 @@ import ( "github.com/ethereum/go-ethereum/p2p/discover" "github.com/ethereum/go-ethereum/p2p/protocols" "github.com/ethereum/go-ethereum/p2p/simulations/adapters" + "github.com/ethereum/go-ethereum/rpc" ) const ( - ProtocolName = "bzz" - Version = 0 NetworkId = 322 // BZZ in l33t ProtocolMaxMsgSize = 10 * 1024 * 1024 ) +var BzzProtocol = &protocols.Spec{ + Name: "bzz", + Version: 1, + MaxMsgSize: 10 * 1024 * 1024, + Messages: []interface{}{ + bzzHandshake{}, + }, +} + +var DiscoveryProtocol = &protocols.Spec{ + Name: "hive", + Version: 1, + MaxMsgSize: 10 * 1024 * 1024, + Messages: []interface{}{ + peersMsg{}, + getPeersMsg{}, + subPeersMsg{}, + }, +} + +var PssProtocol = &protocols.Spec{ + Name: "pss", + Version: 1, + MaxMsgSize: 10 * 1024 * 1024, + Messages: []interface{}{ + PssMsg{}, + }, +} + // the Addr interface that peerPool needs type Addr interface { OverlayPeer @@ -52,13 +83,145 @@ type Peer interface { // Conn interface represents an live peer connection type Conn interface { - ID() discover.NodeID // the key that uniquely identifies the Node for the peerPool - Handshake(interface{}, time.Duration) (interface{}, error) // can send messages - Send(interface{}) error // can send messages - Drop(error) // disconnect this peer - Register(interface{}, func(interface{}) error) uint64 // register message-handler callbacks - DisconnectHook(func(error)) // register message-handler callbacks - Run() error // the run function to run a protocol + ID() discover.NodeID // the key that uniquely identifies the Node for the peerPool + Handshake(context.Context, interface{}) (interface{}, error) // can send messages + Send(interface{}) error // can send messages + Drop(error) // disconnect this peer + Run(func(interface{}) error) error // the run function to run a protocol +} + +// TODO: implement store for exec nodes +type Store interface { + Load(string) ([]byte, error) + Save(string, []byte) error +} + +type BzzConfig struct { + OverlayAddr []byte + UnderlayAddr []byte + + KadParams *KadParams + HiveParams *HiveParams + PssParams *PssParams + + Store Store +} + +func NewBzz(config *BzzConfig) *Bzz { + kademlia := NewKademlia(config.OverlayAddr, config.KadParams) + bzz := &Bzz{ + Kademlia: kademlia, + Hive: NewHive(config.HiveParams, kademlia, config.Store), + localAddr: &bzzAddr{config.OverlayAddr, config.UnderlayAddr}, + handshakes: make(map[discover.NodeID]*bzzHandshake), + } + if config.PssParams != nil { + bzz.Pss = NewPss(kademlia, config.PssParams) + } + return bzz +} + +type Bzz struct { + Kademlia *Kademlia + Hive *Hive + Pss *Pss + + localAddr *bzzAddr + mtx sync.Mutex + handshakes map[discover.NodeID]*bzzHandshake +} + +func (b *Bzz) Protocols() []p2p.Protocol { + return []p2p.Protocol{ + { + Name: BzzProtocol.Name, + Version: BzzProtocol.Version, + Length: BzzProtocol.Length(), + Run: b.runHandshake, + }, + { + Name: DiscoveryProtocol.Name, + Version: DiscoveryProtocol.Version, + Length: DiscoveryProtocol.Length(), + Run: b.runProtocol(DiscoveryProtocol, b.Hive.Run), + NodeInfo: b.Hive.NodeInfo, + PeerInfo: b.Hive.PeerInfo, + }, + { + Name: PssProtocol.Name, + Version: PssProtocol.Version, + Length: PssProtocol.Length(), + Run: b.runProtocol(PssProtocol, b.Pss.Run), + }, + } +} + +func (b *Bzz) APIs() []rpc.API { + return []rpc.API{{ + Namespace: "hive", + Version: "1.0", + Service: b.Hive, + }} +} + +func (b *Bzz) Start(server p2p.Server) error { + return b.Hive.Start(server) +} + +func (b *Bzz) Stop() error { + b.Hive.Stop() + return nil +} + +func (b *Bzz) runHandshake(p *p2p.Peer, rw p2p.MsgReadWriter) error { + handshake := b.getHandshake(p.ID()) + + if err := handshake.Perform(p, rw); err != nil { + log.Error("handshake failed", "peer", p.ID(), "err", err) + return err + } + + // fail if we get another handshake + msg, err := rw.ReadMsg() + if err != nil { + return err + } + msg.Discard() + return errors.New("received multiple handshakes") +} + +func (b *Bzz) runProtocol(spec *protocols.Spec, run func(*bzzPeer) error) func(*p2p.Peer, p2p.MsgReadWriter) error { + return func(p *p2p.Peer, rw p2p.MsgReadWriter) error { + // wait for the bzz protocol to perform the handshake + handshake := b.getHandshake(p.ID()) + if err := handshake.Wait(); err != nil { + return err + } + + // the handshake has succeeded so run the service + peer := &bzzPeer{ + Conn: protocols.NewPeer(p, rw, spec), + localAddr: b.localAddr, + bzzAddr: handshake.peerAddr, + } + return run(peer) + } +} + +func (b *Bzz) getHandshake(peerID discover.NodeID) *bzzHandshake { + b.mtx.Lock() + defer b.mtx.Unlock() + handshake, ok := b.handshakes[peerID] + if !ok { + handshake = &bzzHandshake{ + Version: uint64(BzzProtocol.Version), + NetworkId: uint64(NetworkId), + Addr: b.localAddr, + done: make(chan struct{}), + } + b.handshakes[peerID] = handshake + } + return handshake } // bzzPeer is the bzz protocol view of a protocols.Peer (itself an extension of p2p.Peer) @@ -70,6 +233,13 @@ type bzzPeer struct { lastActive time.Time // time is updated whenever mutexes are releasing } +func newBzzPeer(conn Conn, over, under []byte) *bzzPeer { + return &bzzPeer{ + Conn: conn, + localAddr: &bzzAddr{over, under}, + } +} + // Off returns the overlay peer record for offline persistance func (self *bzzPeer) Off() OverlayAddr { return self.bzzAddr @@ -80,47 +250,6 @@ func (self *bzzPeer) LastActive() time.Time { return self.lastActive } -// BzzCodeMap compiles the message codes and message types bzz wire protocol. -// note each call to Register can start a new series (initial code is arg1) -// the initial offset for a series is arbitrary (to ensure u) -func BzzCodeMap(msgs ...interface{}) *protocols.CodeMap { - ct := protocols.NewCodeMap(ProtocolName, Version, ProtocolMaxMsgSize) - ct.Register(0, &bzzHandshake{}) - ct.Register(1, msgs...) - return ct -} - -// NewBzz is the protocol constructor -// returns p2p.Protocol that is to be offered by the node.Service -func NewBzz(over, under []byte, ct *protocols.CodeMap, services func(*bzzPeer) error, peerInfo func(id discover.NodeID) interface{}, nodeInfo func() interface{}) *p2p.Protocol { - run := func(p *protocols.Peer) error { - bee := &bzzPeer{ - Conn: p, - localAddr: &bzzAddr{over, under}, - } - // protocol handshake and its validation - // sets remote peer address - err := bee.bzzHandshake() - if err != nil { - log.Error(fmt.Sprintf("handshake error in peer %v: %v", bee.ID(), err)) - return err - } - - // mount external service models on the peer connection (swap, sync, hive) - if services != nil { - err = services(bee) - if err != nil { - log.Error(fmt.Sprintf("protocol service error for peer %v: %v", bee.ID(), err)) - return err - } - } - - return bee.Run() - } - - return protocols.NewProtocol(ProtocolName, Version, run, ct, peerInfo, nodeInfo) -} - /* Handshake @@ -132,12 +261,52 @@ type bzzHandshake struct { Version uint64 NetworkId uint64 Addr *bzzAddr + + // peerAddr is the address received in the peer handshake + peerAddr *bzzAddr + + done chan struct{} + err error } func (self *bzzHandshake) String() string { return fmt.Sprintf("Handshake: Version: %v, NetworkId: %v, Addr: %v", self.Version, self.NetworkId, self.Addr) } +const bzzHandshakeTimeout = time.Second + +func (self *bzzHandshake) Perform(p *p2p.Peer, rw p2p.MsgReadWriter) (err error) { + defer func() { + self.err = err + close(self.done) + }() + peer := protocols.NewPeer(p, rw, BzzProtocol) + ctx, cancel := context.WithTimeout(context.Background(), bzzHandshakeTimeout) + defer cancel() + hs, err := peer.Handshake(ctx, self) + if err != nil { + return err + } + rhs := hs.(*bzzHandshake) + if rhs.NetworkId != self.NetworkId { + return fmt.Errorf("network id mismatch %d (!= %d)", rhs.NetworkId, self.NetworkId) + } + if rhs.Version != self.Version { + return fmt.Errorf("version mismatch %d (!= %d)", rhs.Version, self.Version) + } + self.peerAddr = rhs.Addr + return nil +} + +func (self *bzzHandshake) Wait() error { + select { + case <-self.done: + return self.err + case <-time.After(bzzHandshakeTimeout): + return errors.New("timed out waiting for bzz handshake") + } +} + // bzzAddr implements the PeerAddr interface type bzzAddr struct { OAddr []byte @@ -149,6 +318,9 @@ func (self *bzzAddr) Address() []byte { return self.OAddr } +func (self *bzzAddr) Bytes() []byte { + return self.OAddr +} func (self *bzzAddr) Over() []byte { return self.OAddr } @@ -171,47 +343,6 @@ func (self *bzzAddr) String() string { return fmt.Sprintf("%x <%x>", self.OAddr, self.UAddr) } -// bzzHandshake negotiates the bzz master handshake -// and validates the response, returns error when -// mismatch/incompatibility is evident -func (self *bzzPeer) bzzHandshake() error { - - lhs := &bzzHandshake{ - Version: uint64(Version), - NetworkId: uint64(NetworkId), - Addr: self.localAddr, - } - - hs, err := self.Handshake(lhs, time.Second) - if err != nil { - log.Error(fmt.Sprintf("handshake failed: %v", err)) - return err - } - - rhs := hs.(*bzzHandshake) - self.bzzAddr = rhs.Addr - err = checkBzzHandshake(rhs) - if err != nil { - log.Error(fmt.Sprintf("handshake between %v and %v failed: %v", self.localAddr, self.bzzAddr, err)) - return err - } - return nil -} - -// checkBzzHandshake checks for the validity and compatibility of the remote handshake -func checkBzzHandshake(rhs *bzzHandshake) error { - - if NetworkId != rhs.NetworkId { - return fmt.Errorf("network id mismatch %d (!= %d)", rhs.NetworkId, NetworkId) - } - - if Version != rhs.Version { - return fmt.Errorf("version mismatch %d (!= %d)", rhs.Version, Version) - } - - return nil -} - // RandomAddr is a utility method generating an address from a public key func RandomAddr() *bzzAddr { key, err := crypto.GenerateKey() diff --git a/swarm/network/protocol_test.go b/swarm/network/protocol_test.go index cc4372a763..15875eb9ce 100644 --- a/swarm/network/protocol_test.go +++ b/swarm/network/protocol_test.go @@ -2,14 +2,43 @@ package network import ( "fmt" + "sync" "testing" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/protocols" "github.com/ethereum/go-ethereum/p2p/simulations/adapters" p2ptest "github.com/ethereum/go-ethereum/p2p/testing" ) +type testStore struct { + sync.Mutex + + values map[string][]byte +} + +func newTestStore() *testStore { + return &testStore{values: make(map[string][]byte)} +} + +func (t *testStore) Load(key string) ([]byte, error) { + t.Lock() + defer t.Unlock() + v, ok := t.values[key] + if !ok { + return nil, fmt.Errorf("key not found: %s", key) + } + return v, nil +} + +func (t *testStore) Save(key string, v []byte) error { + t.Lock() + defer t.Unlock() + t.values[key] = v + return nil +} + func bzzHandshakeExchange(lhs, rhs *bzzHandshake, id *adapters.NodeId) []p2ptest.Exchange { return []p2ptest.Exchange{ @@ -34,19 +63,21 @@ func bzzHandshakeExchange(lhs, rhs *bzzHandshake, id *adapters.NodeId) []p2ptest } } -func newBzzBaseTester(t *testing.T, n int, addr *bzzAddr, ct *protocols.CodeMap, services func(*bzzPeer) error) *bzzTester { - if ct == nil { - ct = BzzCodeMap() - } - +func newBzzBaseTester(t *testing.T, n int, addr *bzzAddr, spec *protocols.Spec, run func(*bzzPeer) error) *bzzTester { cs := make(map[string]chan bool) srv := func(p *bzzPeer) error { defer close(cs[p.ID().String()]) - return services(p) + return run(p) } - protocall := NewBzz(addr.Over(), addr.Under(), ct, srv, nil, nil).Run + protocall := func(p *p2p.Peer, rw p2p.MsgReadWriter) error { + return srv(&bzzPeer{ + Conn: protocols.NewPeer(p, rw, spec), + localAddr: addr, + bzzAddr: NewAddrFromNodeId(&adapters.NodeId{NodeID: p.ID()}), + }) + } s := p2ptest.NewProtocolTester(t, NewNodeIdFromAddr(addr), n, protocall) @@ -55,7 +86,7 @@ func newBzzBaseTester(t *testing.T, n int, addr *bzzAddr, ct *protocols.CodeMap, } return &bzzTester{ - addr: addr.Address(), + addr: addr, ProtocolTester: s, cs: cs, } @@ -63,26 +94,18 @@ func newBzzBaseTester(t *testing.T, n int, addr *bzzAddr, ct *protocols.CodeMap, type bzzTester struct { *p2ptest.ProtocolTester - addr []byte + addr *bzzAddr cs map[string]chan bool } -func newBzzTester(t *testing.T, n int, addr *bzzAddr, pp *p2ptest.TestPeerPool, ct *protocols.CodeMap, services func(Peer) error) *bzzTester { +func newBzzTester(t *testing.T, n int, addr *bzzAddr, pp *p2ptest.TestPeerPool, spec *protocols.Spec, services func(Peer) error) *bzzTester { extraservices := func(p *bzzPeer) error { pp.Add(p) - p.DisconnectHook(func(err error) { - pp.Remove(p) - }) - if services != nil { - err := services(p) - if err != nil { - return err - } - } - return nil + defer pp.Remove(p) + return services(p) } - return newBzzBaseTester(t, n, addr, ct, extraservices) + return newBzzBaseTester(t, n, addr, spec, extraservices) } // should test handshakes in one exchange? parallelisation @@ -113,7 +136,11 @@ func (s *bzzTester) runHandshakes(ids ...*adapters.NodeId) { } func correctBzzHandshake(addr *bzzAddr) *bzzHandshake { - return &bzzHandshake{0, 322, addr} + return &bzzHandshake{ + Version: 0, + NetworkId: 322, + Addr: addr, + } } func TestBzzHandshakeNetworkIdMismatch(t *testing.T) { @@ -125,7 +152,7 @@ func TestBzzHandshakeNetworkIdMismatch(t *testing.T) { id := s.Ids[0] s.testHandshake( correctBzzHandshake(addr), - &bzzHandshake{0, 321, NewAddrFromNodeId(id)}, + &bzzHandshake{Version: 0, NetworkId: 321, Addr: NewAddrFromNodeId(id)}, &p2ptest.Disconnect{Peer: id, Error: fmt.Errorf("network id mismatch 321 (!= 322)")}, ) } @@ -139,7 +166,7 @@ func TestBzzHandshakeVersionMismatch(t *testing.T) { id := s.Ids[0] s.testHandshake( correctBzzHandshake(addr), - &bzzHandshake{1, 322, NewAddrFromNodeId(id)}, + &bzzHandshake{Version: 1, NetworkId: 322, Addr: NewAddrFromNodeId(id)}, &p2ptest.Disconnect{Peer: id, Error: fmt.Errorf("version mismatch 1 (!= 0)")}, ) } @@ -153,7 +180,7 @@ func TestBzzHandshakeSuccess(t *testing.T) { id := s.Ids[0] s.testHandshake( correctBzzHandshake(addr), - &bzzHandshake{0, 322, NewAddrFromNodeId(id)}, + &bzzHandshake{Version: 0, NetworkId: 322, Addr: NewAddrFromNodeId(id)}, ) } @@ -215,7 +242,7 @@ func TestBzzPeerPoolNotAdd(t *testing.T) { defer s.Stop() id := s.Ids[0] - s.testHandshake(correctBzzHandshake(addr), &bzzHandshake{0, 321, NewAddrFromNodeId(id)}, &p2ptest.Disconnect{Peer: id, Error: fmt.Errorf("network id mismatch 321 (!= 322)")}) + s.testHandshake(correctBzzHandshake(addr), &bzzHandshake{Version: 0, NetworkId: 321, Addr: NewAddrFromNodeId(id)}, &p2ptest.Disconnect{Peer: id, Error: fmt.Errorf("network id mismatch 321 (!= 322)")}) if pp.Has(id) { t.Fatalf("peer %v incorrectly added: %v", id, pp) } diff --git a/swarm/network/pss.go b/swarm/network/pss.go index fa1265982a..d713c1d764 100644 --- a/swarm/network/pss.go +++ b/swarm/network/pss.go @@ -121,6 +121,7 @@ type pssDigest uint32 // - a message cache to spot messages that previously have been forwarded type Pss struct { Overlay // we can get the overlayaddress from this + //peerPool map[pot.Address]map[PssTopic]*PssReadWriter // keep track of all virtual p2p.Peers we are currently speaking to peerPool map[pot.Address]map[PssTopic]p2p.MsgReadWriter // keep track of all virtual p2p.Peers we are currently speaking to handlers map[PssTopic]func([]byte, *p2p.Peer, []byte) error // topic and version based pss payload handlers @@ -162,6 +163,20 @@ func NewPss(k Overlay, params *PssParams) *Pss { } } +func (p *Pss) Run(peer *bzzPeer) error { + return peer.Run(p.HandleMsg) +} + +func (p *Pss) HandleMsg(m interface{}) error { + msg, ok := m.(*PssMsg) + if !ok { + return fmt.Errorf("unknown pss protocol message type: %T", m) + } + _ = msg + // TODO: handle the message + return nil +} + // enables to set address of node, to avoid backwards forwarding // // currently not in use as forwarder address is not known in the handler function hooked to the pss dispatcher. @@ -380,7 +395,7 @@ type PssReadWriter struct { RecipientOAddr pot.Address LastActive time.Time rw chan p2p.Msg - ct *protocols.CodeMap + spec *protocols.Spec topic *PssTopic } @@ -396,7 +411,7 @@ func (prw PssReadWriter) ReadMsg() (p2p.Msg, error) { // Implements p2p.MsgWriter func (prw PssReadWriter) WriteMsg(msg p2p.Msg) error { log.Trace(fmt.Sprintf("pssrw writemsg: %v", msg)) - ifc, found := prw.ct.GetInterface(msg.Code) + ifc, found := prw.spec.NewMsg(msg.Code) if !found { return fmt.Errorf("Writemsg couldn't find matching interface for code %d", msg.Code) } @@ -417,20 +432,20 @@ func (prw PssReadWriter) injectMsg(msg p2p.Msg) error { } // Convenience object for passing messages in and out of the p2p layer -type PssProtocol struct { +type pssProtocol struct { *Pss virtualProtocol *p2p.Protocol topic *PssTopic - ct *protocols.CodeMap + spec *protocols.Spec } // Constructor -func NewPssProtocol(pss *Pss, topic *PssTopic, ct *protocols.CodeMap, targetprotocol *p2p.Protocol) *PssProtocol { - pp := &PssProtocol{ +func NewPssProtocol(pss *Pss, topic *PssTopic, spec *protocols.Spec, targetprotocol *p2p.Protocol) *pssProtocol { + pp := &pssProtocol{ Pss: pss, virtualProtocol: targetprotocol, topic: topic, - ct: ct, + spec: spec, } return pp } @@ -438,18 +453,18 @@ func NewPssProtocol(pss *Pss, topic *PssTopic, ct *protocols.CodeMap, targetprot // Retrieves a convenience method for passing an incoming message into the p2p layer // // If the implementer wishes to use the p2p.Protocol (or p2p/protocols) message handling, this handler can be directly registered as a handler for the PssMsg structure -func (self *PssProtocol) GetHandler() func([]byte, *p2p.Peer, []byte) error { +func (self *pssProtocol) GetHandler() func([]byte, *p2p.Peer, []byte) error { return self.handle } -func (self *PssProtocol) handle(msg []byte, p *p2p.Peer, senderAddr []byte) error { +func (self *pssProtocol) handle(msg []byte, p *p2p.Peer, senderAddr []byte) error { hashoaddr := pot.NewHashAddressFromBytes(senderAddr).Address if !self.isActive(hashoaddr, *self.topic) { rw := &PssReadWriter{ Pss: self.Pss, RecipientOAddr: hashoaddr, rw: make(chan p2p.Msg), - ct: self.ct, + spec: self.spec, topic: self.topic, } self.Pss.AddPeer(p, hashoaddr, self.virtualProtocol.Run, *self.topic, rw) diff --git a/swarm/network/pss_test.go b/swarm/network/pss_test.go index 68b3f2abfa..502558b0a0 100644 --- a/swarm/network/pss_test.go +++ b/swarm/network/pss_test.go @@ -1,25 +1,9 @@ package network import ( - "context" - "encoding/hex" - "fmt" - "math/rand" - "net" - "net/http" "os" - "strconv" - "testing" - "time" - "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/node" - "github.com/ethereum/go-ethereum/p2p" - "github.com/ethereum/go-ethereum/p2p/protocols" - "github.com/ethereum/go-ethereum/p2p/simulations" - "github.com/ethereum/go-ethereum/p2p/simulations/adapters" - "github.com/ethereum/go-ethereum/rpc" ) const ( @@ -32,6 +16,7 @@ func init() { log.Root().SetHandler(h) } +/* // example protocol implementation peer // message handlers are methods of this // channels allow receipt reporting from p2p.Protocol message handler @@ -66,10 +51,6 @@ func (n *pssTestNode) Add(peer *bzzPeer) error { return err } -func (n *pssTestNode) hiveKeepAlive() <-chan time.Time { - return time.Tick(time.Millisecond * 300) -} - func (n *pssTestNode) triggerCheck() { go func() { n.trigger <- n.id }() } @@ -98,8 +79,9 @@ type pssTestService struct { func newPssTestService(t *testing.T, handlefunc func(interface{}) error, testnode *pssTestNode) *pssTestService { hp := NewHiveParams() - //hp.CallInterval = 250 - testnode.Hive = NewHive(hp, testnode.Pss.Overlay) + hp.KeepAliveInterval = 300 + bzz := NewBzz(testnode.OverlayAddr(), testnode.UnderlayAddr(), newTestStore()) + testnode.Hive = NewHive(hp, testnode.Pss.Overlay, bzz) return &pssTestService{ //nid := adapters.NewNodeId(addr.UnderlayAddr()) msgFunc: handlefunc, @@ -108,7 +90,7 @@ func newPssTestService(t *testing.T, handlefunc func(interface{}) error, testnod } func (self *pssTestService) Start(server p2p.Server) error { - return self.node.Hive.Start(server, self.node.hiveKeepAlive, nil) + return self.node.Hive.Start(server) } func (self *pssTestService) Stop() error { @@ -117,24 +99,13 @@ func (self *pssTestService) Stop() error { } func (self *pssTestService) Protocols() []p2p.Protocol { - ct := BzzCodeMap() - ct.Register(0, &PssMsg{}) - for _, m := range DiscoveryMsgs { - ct.Register(1, m) - } - - srv := func(p *bzzPeer) error { - p.Register(&PssMsg{}, self.msgFunc) - self.node.Add(p) - p.DisconnectHook(func(err error) { - self.node.Remove(p) - }) - return nil - } - - proto := NewBzz(self.node.OverlayAddr(), self.node.UnderlayAddr(), ct, srv, nil, nil) - - return []p2p.Protocol{*proto} + bzz := NewBzz(self.node.OverlayAddr(), self.node.UnderlayAddr(), newTestStore()) + return append(self.node.Hive.Protocols(), p2p.Protocol{ + Name: PssProtocolName, + Version: PssProtocolVersion, + Length: PssProtocol.Length(), + Run: bzz.RunProtocol(PssProtocol, self.Run), + }) } func (self *pssTestService) APIs() []rpc.API { @@ -149,6 +120,12 @@ func (self *pssTestService) APIs() []rpc.API { return nil } +func (self *pssTestService) Run(peer *bzzPeer) error { + self.node.Add(peer) + defer self.node.Remove(peer) + return peer.Run(self.msgFunc) +} + func TestPssCache(t *testing.T) { var err error to, _ := hex.DecodeString("08090a0b0c0d0e0f1011121314150001020304050607161718191a1b1c1d1e1f") @@ -301,8 +278,9 @@ func testPssFullRandom(t *testing.T, numsends int, numnodes int, numfullnodes in expectnodesids := []*adapters.NodeId{} // the nodes to expect on (needed by checker) expectnodesresults := make(map[*adapters.NodeId][]int) // which messages expect actually got - vct := protocols.NewCodeMap(protocolName, protocolVersion, ProtocolMaxMsgSize) - vct.Register(0, &pssTestPayload{}) + vct := protocols.NewCodeMap(map[uint64]interface{}{ + 0: pssTestPayload{}, + }) topic, _ := MakeTopic(protocolName, protocolVersion) trigger := make(chan *adapters.NodeId) @@ -536,15 +514,15 @@ func TestPssFullLinearEcho(t *testing.T) { return err } - /*for i, id := range ids { - var peerId *adapters.NodeId - if i != 0 { - peerId = ids[i-1] - if err := net.Connect(id, peerId); err != nil { - return err - } - } - }*/ + // for i, id := range ids { + // var peerId *adapters.NodeId + // if i != 0 { + // peerId = ids[i-1] + // if err := net.Connect(id, peerId); err != nil { + // return err + // } + // } + // } return nil } check = func(ctx context.Context, id *adapters.NodeId) (bool, error) { @@ -1108,3 +1086,4 @@ func (ptp *pssTestPeer) SimpleHandlePssPayload(msg interface{}) error { return nil } +*/ diff --git a/swarm/network/simulations/discovery/discovery_test.go b/swarm/network/simulations/discovery/discovery_test.go index 22d97efec0..c436f20025 100644 --- a/swarm/network/simulations/discovery/discovery_test.go +++ b/swarm/network/simulations/discovery/discovery_test.go @@ -9,11 +9,10 @@ import ( "time" "github.com/ethereum/go-ethereum/log" - p2pnode "github.com/ethereum/go-ethereum/node" + "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/simulations" "github.com/ethereum/go-ethereum/p2p/simulations/adapters" - "github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/swarm/network" ) @@ -22,9 +21,7 @@ import ( const serviceName = "discovery" var services = adapters.Services{ - serviceName: func(id *adapters.NodeId, snapshot []byte) p2pnode.Service { - return newNode(id) - }, + serviceName: newService, } func init() { @@ -70,7 +67,7 @@ func testDiscoverySimulation(t *testing.T, adapter adapters.NodeAdapter) { for i := 0; i < nodeCount; i++ { node, err := net.NewNode() if err != nil { - t.Fatalf("error starting node %s: %s", node.ID().Label(), err) + t.Fatalf("error starting node: %s", err) } if err := net.Start(node.ID()); err != nil { t.Fatalf("error starting node %s: %s", node.ID().Label(), err) @@ -179,70 +176,24 @@ func triggerChecks(trigger chan *adapters.NodeId, net *simulations.Network, id * return nil } -type node struct { - *network.Hive +func newService(id *adapters.NodeId, snapshot []byte) node.Service { + addr := network.NewAddrFromNodeId(id) - protocol *p2p.Protocol -} - -func newNode(id *adapters.NodeId) *node { - addr := network.NewPeerAddrFromNodeId(id) - kademlia := newKademlia(addr.OverlayAddr()) - hive := newHive(kademlia) - codeMap := network.BzzCodeMap(network.DiscoveryMsgs...) - node := &node{Hive: hive} - services := func(peer network.Peer) error { - discoveryPeer := network.NewDiscovery(peer, kademlia) - node.Add(discoveryPeer) - peer.DisconnectHook(func(err error) { - node.Remove(discoveryPeer) - }) - return nil + config := &network.BzzConfig{ + OverlayAddr: addr.Over(), + UnderlayAddr: addr.Under(), + KadParams: network.NewKadParams(), + HiveParams: network.NewHiveParams(), } - node.protocol = network.Bzz(addr.OverlayAddr(), addr.UnderlayAddr(), codeMap, services, nil, nil) - return node -} - -func newKademlia(overlayAddr []byte) *network.Kademlia { - params := network.NewKadParams() - params.MinProxBinSize = 2 - params.MaxBinSize = 3 - params.MinBinSize = 1 - params.MaxRetries = 1000 - params.RetryExponent = 2 - params.RetryInterval = 1000000 - - return network.NewKademlia(overlayAddr, params) -} - -func newHive(kademlia *network.Kademlia) *network.Hive { - params := network.NewHiveParams() - params.CallInterval = 5000 - - return network.NewHive(params, kademlia) -} - -func (n *node) Protocols() []p2p.Protocol { - return []p2p.Protocol{*n.protocol} -} - -func (n *node) APIs() []rpc.API { - return []rpc.API{{ - Namespace: "hive", - Version: "1.0", - Service: n.Hive, - }} -} - -func (n *node) Start(server p2p.Server) error { - return n.Hive.Start(server, n.hiveKeepAlive) -} - -func (n *node) Stop() error { - n.Hive.Stop() - return nil -} - -func (n *node) hiveKeepAlive() <-chan time.Time { - return time.Tick(time.Second) + + config.KadParams.MinProxBinSize = 2 + config.KadParams.MaxBinSize = 3 + config.KadParams.MinBinSize = 1 + config.KadParams.MaxRetries = 1000 + config.KadParams.RetryExponent = 2 + config.KadParams.RetryInterval = 1000000 + + config.HiveParams.KeepAliveInterval = time.Second + + return network.NewBzz(config) } From 3ba68dc7af1d19bebcf7bf10177e0f40423fba59 Mon Sep 17 00:00:00 2001 From: Lewis Marshall Date: Sun, 14 May 2017 16:45:41 -0700 Subject: [PATCH 04/24] p2p: Change p2p.Server back to a concrete type --- cmd/swarm/main.go | 2 +- cmd/wnode/main.go | 26 ++++++----- contracts/release/release.go | 2 +- eth/backend.go | 4 +- ethstats/ethstats.go | 4 +- internal/ethapi/api.go | 4 +- les/backend.go | 2 +- les/handler.go | 10 ++-- les/server.go | 2 +- les/serverpool.go | 8 ++-- node/api.go | 2 +- node/node.go | 10 ++-- node/node_example_test.go | 2 +- node/node_test.go | 10 ++-- node/service.go | 2 +- node/utils_test.go | 6 +-- p2p/dial.go | 12 ++--- p2p/server.go | 58 +++++++++--------------- p2p/server_test.go | 12 ++--- p2p/simulations/examples/connectivity.go | 2 +- p2p/simulations/http_test.go | 2 +- p2p/testing/protocolsession.go | 3 +- p2p/testing/protocoltester.go | 6 ++- swarm/network/hive.go | 2 +- swarm/network/hive_test.go | 2 +- swarm/network/protocol.go | 2 +- swarm/network/pss_test.go | 2 +- swarm/network/simulations/overlay.go | 2 +- swarm/swarm.go | 2 +- whisper/whisperv2/main.go | 18 ++++---- whisper/whisperv2/whisper.go | 2 +- whisper/whisperv5/peer_test.go | 26 ++++++----- whisper/whisperv5/whisper.go | 2 +- 33 files changed, 120 insertions(+), 131 deletions(-) diff --git a/cmd/swarm/main.go b/cmd/swarm/main.go index aaf2e3ad28..e6da14c83e 100644 --- a/cmd/swarm/main.go +++ b/cmd/swarm/main.go @@ -459,7 +459,7 @@ func getPassPhrase(prompt string, i int, passwords []string) string { return password } -func injectBootnodes(srv p2p.Server, nodes []string) { +func injectBootnodes(srv *p2p.Server, nodes []string) { for _, url := range nodes { n, err := discover.ParseNode(url) if err != nil { diff --git a/cmd/wnode/main.go b/cmd/wnode/main.go index 5d5aa77ee8..f18025dff8 100644 --- a/cmd/wnode/main.go +++ b/cmd/wnode/main.go @@ -51,7 +51,7 @@ const quitCommand = "~Q" // singletons var ( - server p2p.Server + server *p2p.Server shh *whisper.Whisper done chan struct{} mailServer mailserver.WMailServer @@ -253,17 +253,19 @@ func initialize() { maxPeers = 800 } - server = p2p.NewServer(p2p.Config{ - PrivateKey: nodeid, - MaxPeers: maxPeers, - Name: common.MakeName("wnode", "5.0"), - Protocols: shh.Protocols(), - ListenAddr: *argIP, - NAT: nat.Any(), - BootstrapNodes: peers, - StaticNodes: peers, - TrustedNodes: peers, - }) + server = &p2p.Server{ + Config: p2p.Config{ + PrivateKey: nodeid, + MaxPeers: maxPeers, + Name: common.MakeName("wnode", "5.0"), + Protocols: shh.Protocols(), + ListenAddr: *argIP, + NAT: nat.Any(), + BootstrapNodes: peers, + StaticNodes: peers, + TrustedNodes: peers, + }, + } } func startServer() { diff --git a/contracts/release/release.go b/contracts/release/release.go index e0441fd41e..28a35381d4 100644 --- a/contracts/release/release.go +++ b/contracts/release/release.go @@ -94,7 +94,7 @@ func (r *ReleaseService) Protocols() []p2p.Protocol { return nil } func (r *ReleaseService) APIs() []rpc.API { return nil } // Start spawns the periodic version checker goroutine -func (r *ReleaseService) Start(server p2p.Server) error { +func (r *ReleaseService) Start(server *p2p.Server) error { go r.checker() return nil } diff --git a/eth/backend.go b/eth/backend.go index 9361cb3064..f864b1d88b 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -49,7 +49,7 @@ import ( ) type LesServer interface { - Start(srvr p2p.Server) + Start(srvr *p2p.Server) Stop() Protocols() []p2p.Protocol } @@ -362,7 +362,7 @@ func (s *Ethereum) Protocols() []p2p.Protocol { // Start implements node.Service, starting all internal goroutines needed by the // Ethereum protocol implementation. -func (s *Ethereum) Start(srvr p2p.Server) error { +func (s *Ethereum) Start(srvr *p2p.Server) error { s.netRPCService = ethapi.NewPublicNetAPI(srvr, s.NetVersion()) s.protocolManager.Start() diff --git a/ethstats/ethstats.go b/ethstats/ethstats.go index f295fb60d0..8765da8faf 100644 --- a/ethstats/ethstats.go +++ b/ethstats/ethstats.go @@ -52,7 +52,7 @@ const historyUpdateRange = 50 type Service struct { stack *node.Node // Temporary workaround, remove when API finalized - server p2p.Server // Peer-to-peer server to retrieve networking infos + server *p2p.Server // Peer-to-peer server to retrieve networking infos eth *eth.Ethereum // Full Ethereum service if monitoring a full node les *les.LightEthereum // Light Ethereum service if monitoring a light node engine consensus.Engine // Consensus engine to retrieve variadic block fields @@ -101,7 +101,7 @@ func (s *Service) Protocols() []p2p.Protocol { return nil } func (s *Service) APIs() []rpc.API { return nil } // Start implements node.Service, starting up the monitoring and reporting daemon. -func (s *Service) Start(server p2p.Server) error { +func (s *Service) Start(server *p2p.Server) error { s.server = server go s.loop() diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 146e7f0475..f9eed87975 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -1434,12 +1434,12 @@ func (api *PrivateDebugAPI) SetHead(number hexutil.Uint64) { // PublicNetAPI offers network related RPC methods type PublicNetAPI struct { - net p2p.Server + net *p2p.Server networkVersion uint64 } // NewPublicNetAPI creates a new net API instance. -func NewPublicNetAPI(net p2p.Server, networkVersion uint64) *PublicNetAPI { +func NewPublicNetAPI(net *p2p.Server, networkVersion uint64) *PublicNetAPI { return &PublicNetAPI{net, networkVersion} } diff --git a/les/backend.go b/les/backend.go index 3dac7f15fc..646c81a7b1 100644 --- a/les/backend.go +++ b/les/backend.go @@ -185,7 +185,7 @@ func (s *LightEthereum) Protocols() []p2p.Protocol { // Start implements node.Service, starting all internal goroutines needed by the // Ethereum protocol implementation. -func (s *LightEthereum) Start(srvr p2p.Server) error { +func (s *LightEthereum) Start(srvr *p2p.Server) error { log.Warn("Light client mode is an experimental feature") s.netRPCService = ethapi.NewPublicNetAPI(srvr, s.networkId) s.protocolManager.Start(srvr) diff --git a/les/handler.go b/les/handler.go index 5af89d4fca..64023af0f5 100644 --- a/les/handler.go +++ b/les/handler.go @@ -61,10 +61,6 @@ const ( disableClientRemovePeer = false ) -type discV5Server interface { - DiscV5() *discv5.Network -} - // errIncompatibleConfig is returned if the requested protocols and configs are // not compatible (low protocol version restrictions and high requirements). var errIncompatibleConfig = errors.New("incompatible configuration") @@ -260,10 +256,10 @@ func (pm *ProtocolManager) removePeer(id string) { } } -func (pm *ProtocolManager) Start(srvr p2p.Server) { +func (pm *ProtocolManager) Start(srvr *p2p.Server) { var topicDisc *discv5.Network - if v, ok := srvr.(discV5Server); ok { - topicDisc = v.DiscV5() + if srvr != nil { + topicDisc = srvr.DiscV5 } lesTopic := discv5.Topic("LES@" + common.Bytes2Hex(pm.blockchain.Genesis().Hash().Bytes()[0:8])) if pm.lightSync { diff --git a/les/server.go b/les/server.go index 1be61ef53a..22fe59b7ac 100644 --- a/les/server.go +++ b/les/server.go @@ -68,7 +68,7 @@ func (s *LesServer) Protocols() []p2p.Protocol { } // Start starts the LES server -func (s *LesServer) Start(srvr p2p.Server) { +func (s *LesServer) Start(srvr *p2p.Server) { s.protocolManager.Start(srvr) } diff --git a/les/serverpool.go b/les/serverpool.go index 14a7354261..64fe991c63 100644 --- a/les/serverpool.go +++ b/les/serverpool.go @@ -97,7 +97,7 @@ const ( type serverPool struct { db ethdb.Database dbKey []byte - server p2p.Server + server *p2p.Server quit chan struct{} wg *sync.WaitGroup connWg sync.WaitGroup @@ -118,7 +118,7 @@ type serverPool struct { } // newServerPool creates a new serverPool instance -func newServerPool(db ethdb.Database, dbPrefix []byte, server p2p.Server, topic discv5.Topic, quit chan struct{}, wg *sync.WaitGroup) *serverPool { +func newServerPool(db ethdb.Database, dbPrefix []byte, server *p2p.Server, topic discv5.Topic, quit chan struct{}, wg *sync.WaitGroup) *serverPool { pool := &serverPool{ db: db, dbKey: append(dbPrefix, []byte(topic)...), @@ -139,11 +139,11 @@ func newServerPool(db ethdb.Database, dbPrefix []byte, server p2p.Server, topic pool.loadNodes() pool.checkDial() - if srv, ok := pool.server.(discV5Server); ok && srv.DiscV5() != nil { + if pool.server.DiscV5 != nil { pool.discSetPeriod = make(chan time.Duration, 1) pool.discNodes = make(chan *discv5.Node, 100) pool.discLookups = make(chan bool, 100) - go srv.DiscV5().SearchTopic(topic, pool.discSetPeriod, pool.discNodes, pool.discLookups) + go pool.server.DiscV5.SearchTopic(topic, pool.discSetPeriod, pool.discNodes, pool.discLookups) } go pool.eventLoop() diff --git a/node/api.go b/node/api.go index ba8cc1b8b7..1b2ef2ed90 100644 --- a/node/api.go +++ b/node/api.go @@ -375,7 +375,7 @@ func NewPublicWeb3API(stack *Node) *PublicWeb3API { // ClientVersion returns the node name func (s *PublicWeb3API) ClientVersion() string { - return s.stack.serverConfig.Name + return s.stack.Server().Name } // Sha3 applies the ethereum sha3 implementation on the input. diff --git a/node/node.go b/node/node.go index 7b4988a9cf..a89110599f 100644 --- a/node/node.go +++ b/node/node.go @@ -56,7 +56,7 @@ type Node struct { instanceDirLock storage.Storage // prevents concurrent use of instance directory serverConfig p2p.Config - server p2p.Server // Currently running P2P networking layer + server *p2p.Server // Currently running P2P networking layer serviceFuncs []ServiceConstructor // Service constructors (in dependency order) services map[reflect.Type]Service // Currently running services @@ -165,6 +165,8 @@ func (n *Node) Start() error { if n.serverConfig.NodeDatabase == "" { n.serverConfig.NodeDatabase = n.config.NodeDB() } + running := &p2p.Server{Config: n.serverConfig} + log.Info("Starting peer-to-peer node", "instance", n.serverConfig.Name) // Otherwise copy and specialize the P2P configuration services := make(map[reflect.Type]Service) @@ -192,10 +194,8 @@ func (n *Node) Start() error { } // Gather the protocols and start the freshly assembled P2P server for _, service := range services { - n.serverConfig.Protocols = append(n.serverConfig.Protocols, service.Protocols()...) + running.Protocols = append(running.Protocols, service.Protocols()...) } - running := p2p.NewServer(n.serverConfig) - log.Info("Starting peer-to-peer node", "instance", n.serverConfig.Name) if err := running.Start(); err != nil { if errno, ok := err.(syscall.Errno); ok && datadirInUseErrnos[uint(errno)] { return ErrDatadirUsed @@ -582,7 +582,7 @@ func (n *Node) RPCHandler() (*rpc.Server, error) { // Server retrieves the currently running P2P network layer. This method is meant // only to inspect fields of the currently running server, life cycle management // should be left to this Node entity. -func (n *Node) Server() p2p.Server { +func (n *Node) Server() *p2p.Server { n.lock.RLock() defer n.lock.RUnlock() diff --git a/node/node_example_test.go b/node/node_example_test.go index ddf76faea7..ee06f4065c 100644 --- a/node/node_example_test.go +++ b/node/node_example_test.go @@ -37,7 +37,7 @@ type SampleService struct{} func (s *SampleService) Protocols() []p2p.Protocol { return nil } func (s *SampleService) APIs() []rpc.API { return nil } -func (s *SampleService) Start(p2p.Server) error { fmt.Println("Service starting..."); return nil } +func (s *SampleService) Start(*p2p.Server) error { fmt.Println("Service starting..."); return nil } func (s *SampleService) Stop() error { fmt.Println("Service stopping..."); return nil } func ExampleService() { diff --git a/node/node_test.go b/node/node_test.go index a0f13ca570..2880efa619 100644 --- a/node/node_test.go +++ b/node/node_test.go @@ -154,7 +154,7 @@ func TestServiceLifeCycle(t *testing.T) { id := id // Closure for the constructor constructor := func(*ServiceContext) (Service, error) { return &InstrumentedService{ - startHook: func(p2p.Server) { started[id] = true }, + startHook: func(*p2p.Server) { started[id] = true }, stopHook: func() { stopped[id] = true }, }, nil } @@ -200,7 +200,7 @@ func TestServiceRestarts(t *testing.T) { running = false return &InstrumentedService{ - startHook: func(p2p.Server) { + startHook: func(*p2p.Server) { if running { panic("already running") } @@ -250,7 +250,7 @@ func TestServiceConstructionAbortion(t *testing.T) { id := id // Closure for the constructor constructor := func(*ServiceContext) (Service, error) { return &InstrumentedService{ - startHook: func(p2p.Server) { started[id] = true }, + startHook: func(*p2p.Server) { started[id] = true }, }, nil } if err := stack.Register(maker(constructor)); err != nil { @@ -299,7 +299,7 @@ func TestServiceStartupAbortion(t *testing.T) { id := id // Closure for the constructor constructor := func(*ServiceContext) (Service, error) { return &InstrumentedService{ - startHook: func(p2p.Server) { started[id] = true }, + startHook: func(*p2p.Server) { started[id] = true }, stopHook: func() { stopped[id] = true }, }, nil } @@ -352,7 +352,7 @@ func TestServiceTerminationGuarantee(t *testing.T) { id := id // Closure for the constructor constructor := func(*ServiceContext) (Service, error) { return &InstrumentedService{ - startHook: func(p2p.Server) { started[id] = true }, + startHook: func(*p2p.Server) { started[id] = true }, stopHook: func() { stopped[id] = true }, }, nil } diff --git a/node/service.go b/node/service.go index 05320b75ab..5e1eb0e645 100644 --- a/node/service.go +++ b/node/service.go @@ -86,7 +86,7 @@ type Service interface { // Start is called after all services have been constructed and the networking // layer was also initialized to spawn any goroutines required by the service. - Start(server p2p.Server) error + Start(server *p2p.Server) error // Stop terminates all goroutines belonging to the service, blocking until they // are all terminated. diff --git a/node/utils_test.go b/node/utils_test.go index e9c999b93c..7cdfc2b3aa 100644 --- a/node/utils_test.go +++ b/node/utils_test.go @@ -31,7 +31,7 @@ type NoopService struct{} func (s *NoopService) Protocols() []p2p.Protocol { return nil } func (s *NoopService) APIs() []rpc.API { return nil } -func (s *NoopService) Start(p2p.Server) error { return nil } +func (s *NoopService) Start(*p2p.Server) error { return nil } func (s *NoopService) Stop() error { return nil } func NewNoopService(*ServiceContext) (Service, error) { return new(NoopService), nil } @@ -57,7 +57,7 @@ type InstrumentedService struct { stop error protocolsHook func() - startHook func(p2p.Server) + startHook func(*p2p.Server) stopHook func() } @@ -74,7 +74,7 @@ func (s *InstrumentedService) APIs() []rpc.API { return s.apis } -func (s *InstrumentedService) Start(server p2p.Server) error { +func (s *InstrumentedService) Start(server *p2p.Server) error { if s.startHook != nil { s.startHook(server) } diff --git a/p2p/dial.go b/p2p/dial.go index 10f16f5166..9d58caa103 100644 --- a/p2p/dial.go +++ b/p2p/dial.go @@ -97,7 +97,7 @@ type pastDial struct { } type task interface { - Do(*server) + Do(*Server) } // A dialTask is generated for each node that is dialed. Its @@ -280,7 +280,7 @@ func (s *dialstate) taskDone(t task, now time.Time) { } } -func (t *dialTask) Do(srv *server) { +func (t *dialTask) Do(srv *Server) { if t.dest.Incomplete() { if !t.resolve(srv) { return @@ -301,7 +301,7 @@ func (t *dialTask) Do(srv *server) { // Resolve operations are throttled with backoff to avoid flooding the // discovery network with useless queries for nodes that don't exist. // The backoff delay resets when the node is found. -func (t *dialTask) resolve(srv *server) bool { +func (t *dialTask) resolve(srv *Server) bool { if srv.ntab == nil { log.Debug("Can't resolve node", "id", t.dest.ID, "err", "discovery is disabled") return false @@ -330,7 +330,7 @@ func (t *dialTask) resolve(srv *server) bool { } // dial performs the actual connection attempt. -func (t *dialTask) dial(srv *server, dest *discover.Node) bool { +func (t *dialTask) dial(srv *Server, dest *discover.Node) bool { fd, err := srv.Dialer.Dial(dest) if err != nil { log.Trace("Dial error", "task", t, "err", err) @@ -345,7 +345,7 @@ func (t *dialTask) String() string { return fmt.Sprintf("%v %x %v:%d", t.flags, t.dest.ID[:8], t.dest.IP, t.dest.TCP) } -func (t *discoverTask) Do(srv *server) { +func (t *discoverTask) Do(srv *Server) { // newTasks generates a lookup task whenever dynamic dials are // necessary. Lookups need to take some time, otherwise the // event loop spins too fast. @@ -367,7 +367,7 @@ func (t *discoverTask) String() string { return s } -func (t waitExpireTask) Do(*server) { +func (t waitExpireTask) Do(*Server) { time.Sleep(t.Duration) } func (t waitExpireTask) String() string { diff --git a/p2p/server.go b/p2p/server.go index 14a987d875..eb7c70e593 100644 --- a/p2p/server.go +++ b/p2p/server.go @@ -141,24 +141,8 @@ type Config struct { EnableMsgEvents bool } -type Server interface { - Start() error - Stop() error - SetupConn(net.Conn, connFlag, *discover.Node) - AddPeer(node *discover.Node) - RemovePeer(node *discover.Node) - SubscribeEvents(ch chan *PeerEvent) event.Subscription - PeerCount() int - NodeInfo() *NodeInfo - PeersInfo() []*PeerInfo -} - -func NewServer(conf Config) Server { - return &server{Config: conf} -} - // Server manages all peer connections. -type server struct { +type Server struct { // Config fields may not be modified while the server is running. Config @@ -267,7 +251,7 @@ func (c *conn) is(f connFlag) bool { } // Peers returns all connected peers. -func (srv *server) Peers() []*Peer { +func (srv *Server) Peers() []*Peer { var ps []*Peer select { // Note: We'd love to put this function into a variable but @@ -285,7 +269,7 @@ func (srv *server) Peers() []*Peer { } // PeerCount returns the number of connected peers. -func (srv *server) PeerCount() int { +func (srv *Server) PeerCount() int { var count int select { case srv.peerOp <- func(ps map[discover.NodeID]*Peer) { count = len(ps) }: @@ -298,7 +282,7 @@ func (srv *server) PeerCount() int { // AddPeer connects to the given node and maintains the connection until the // server is shut down. If the connection fails for any reason, the server will // attempt to reconnect the peer. -func (srv *server) AddPeer(node *discover.Node) { +func (srv *Server) AddPeer(node *discover.Node) { select { case srv.addstatic <- node: case <-srv.quit: @@ -306,7 +290,7 @@ func (srv *server) AddPeer(node *discover.Node) { } // RemovePeer disconnects from the given node -func (srv *server) RemovePeer(node *discover.Node) { +func (srv *Server) RemovePeer(node *discover.Node) { select { case srv.removestatic <- node: case <-srv.quit: @@ -314,12 +298,12 @@ func (srv *server) RemovePeer(node *discover.Node) { } // SubscribePeers subscribes the given channel to peer events -func (srv *server) SubscribeEvents(ch chan *PeerEvent) event.Subscription { +func (srv *Server) SubscribeEvents(ch chan *PeerEvent) event.Subscription { return srv.peerFeed.Subscribe(ch) } // Self returns the local node's endpoint information. -func (srv *server) Self() *discover.Node { +func (srv *Server) Self() *discover.Node { srv.lock.Lock() defer srv.lock.Unlock() @@ -329,7 +313,7 @@ func (srv *server) Self() *discover.Node { return srv.makeSelf(srv.listener, srv.ntab) } -func (srv *server) makeSelf(listener net.Listener, ntab discoverTable) *discover.Node { +func (srv *Server) makeSelf(listener net.Listener, ntab discoverTable) *discover.Node { // If the server's not running, return an empty node. // If the node is running but discovery is off, manually assemble the node infos. if ntab == nil { @@ -351,7 +335,7 @@ func (srv *server) makeSelf(listener net.Listener, ntab discoverTable) *discover // Stop terminates the server and all active peer connections. // It blocks until all active connections have been closed. -func (srv *server) Stop() error { +func (srv *Server) Stop() error { srv.lock.Lock() defer srv.lock.Unlock() if !srv.running { @@ -369,7 +353,7 @@ func (srv *server) Stop() error { // Start starts running the server. // Servers can not be re-used after stopping. -func (srv *server) Start() (err error) { +func (srv *Server) Start() (err error) { srv.lock.Lock() defer srv.lock.Unlock() if srv.running { @@ -447,7 +431,7 @@ func (srv *server) Start() (err error) { return nil } -func (srv *server) startListening() error { +func (srv *Server) startListening() error { // Launch the TCP listener. listener, err := net.Listen("tcp", srv.ListenAddr) if err != nil { @@ -476,7 +460,7 @@ type dialer interface { removeStatic(*discover.Node) } -func (srv *server) run(dialstate dialer) { +func (srv *Server) run(dialstate dialer) { defer srv.loopWG.Done() var ( peers = make(map[discover.NodeID]*Peer) @@ -617,7 +601,7 @@ running: } } -func (srv *server) protoHandshakeChecks(peers map[discover.NodeID]*Peer, c *conn) error { +func (srv *Server) protoHandshakeChecks(peers map[discover.NodeID]*Peer, c *conn) error { // Drop connections with no matching protocols. if len(srv.Protocols) > 0 && countMatchingProtocols(srv.Protocols, c.caps) == 0 { return DiscUselessPeer @@ -627,7 +611,7 @@ func (srv *server) protoHandshakeChecks(peers map[discover.NodeID]*Peer, c *conn return srv.encHandshakeChecks(peers, c) } -func (srv *server) encHandshakeChecks(peers map[discover.NodeID]*Peer, c *conn) error { +func (srv *Server) encHandshakeChecks(peers map[discover.NodeID]*Peer, c *conn) error { switch { case !c.is(trustedConn|staticDialedConn) && len(peers) >= srv.MaxPeers: return DiscTooManyPeers @@ -646,7 +630,7 @@ type tempError interface { // listenLoop runs in its own goroutine and accepts // inbound connections. -func (srv *server) listenLoop() { +func (srv *Server) listenLoop() { defer srv.loopWG.Done() log.Info("RLPx listener up", "self", srv.makeSelf(srv.listener, srv.ntab)) @@ -707,7 +691,7 @@ func (srv *server) listenLoop() { // setupConn runs the handshakes and attempts to add the connection // as a peer. It returns when the connection has been added as a peer // or the handshakes have failed. -func (srv *server) SetupConn(fd net.Conn, flags connFlag, dialDest *discover.Node) { +func (srv *Server) SetupConn(fd net.Conn, flags connFlag, dialDest *discover.Node) { // Prevent leftover pending conns from entering the handshake. srv.lock.Lock() running := srv.running @@ -767,7 +751,7 @@ func truncateName(s string) string { // checkpoint sends the conn to run, which performs the // post-handshake checks for the stage (posthandshake, addpeer). -func (srv *server) checkpoint(c *conn, stage chan<- *conn) error { +func (srv *Server) checkpoint(c *conn, stage chan<- *conn) error { select { case stage <- c: case <-srv.quit: @@ -784,7 +768,7 @@ func (srv *server) checkpoint(c *conn, stage chan<- *conn) error { // runPeer runs in its own goroutine for each peer. // it waits until the Peer logic returns and removes // the peer. -func (srv *server) runPeer(p *Peer) { +func (srv *Server) runPeer(p *Peer) { if srv.newPeerHook != nil { srv.newPeerHook(p) } @@ -825,7 +809,7 @@ type NodeInfo struct { } // NodeInfo gathers and returns a collection of metadata known about the host. -func (srv *server) NodeInfo() *NodeInfo { +func (srv *Server) NodeInfo() *NodeInfo { node := srv.Self() // Gather and assemble the generic node infos @@ -854,7 +838,7 @@ func (srv *server) NodeInfo() *NodeInfo { } // PeersInfo returns an array of metadata objects describing connected peers. -func (srv *server) PeersInfo() []*PeerInfo { +func (srv *Server) PeersInfo() []*PeerInfo { // Gather all the generic and sub-protocol specific infos infos := make([]*PeerInfo, 0, srv.PeerCount()) for _, peer := range srv.Peers() { @@ -873,6 +857,6 @@ func (srv *server) PeersInfo() []*PeerInfo { return infos } -func (srv *server) DiscV5() *discv5.Network { +func (srv *Server) DiscV5() *discv5.Network { return srv.discV5 } diff --git a/p2p/server_test.go b/p2p/server_test.go index ca8f1873ee..11dd83e5d6 100644 --- a/p2p/server_test.go +++ b/p2p/server_test.go @@ -72,7 +72,7 @@ func startTestServer(t *testing.T, id discover.NodeID, pf func(*Peer)) *Server { ListenAddr: "127.0.0.1:0", PrivateKey: newkey(), } - server := &server{ + server := &Server{ Config: config, newPeerHook: pf, newTransport: func(fd net.Conn) transport { return newTestTransport(id, fd) }, @@ -201,7 +201,7 @@ func TestServerTaskScheduling(t *testing.T) { // The Server in this test isn't actually running // because we're only interested in what run does. - srv := &server{ + srv := &Server{ Config: Config{MaxPeers: 10}, quit: make(chan struct{}), ntab: fakeTable{}, @@ -246,7 +246,7 @@ func TestServerManyTasks(t *testing.T) { } var ( - srv = &server{quit: make(chan struct{}), ntab: fakeTable{}, running: true} + srv = &Server{quit: make(chan struct{}), ntab: fakeTable{}, running: true} done = make(chan *testTask) start, end = 0, 0 ) @@ -317,7 +317,7 @@ func (t *testTask) Do(srv *Server) { // at capacity. Trusted connections should still be accepted. func TestServerAtCap(t *testing.T) { trustedID := randomID() - srv := &server{ + srv := &Server{ Config: Config{ PrivateKey: newkey(), MaxPeers: 10, @@ -420,7 +420,7 @@ func TestServerSetupConn(t *testing.T) { } for i, test := range tests { - srv := &server{ + srv := &Server{ Config: Config{ PrivateKey: srvkey, MaxPeers: 10, @@ -435,7 +435,7 @@ func TestServerSetupConn(t *testing.T) { } } p1, _ := net.Pipe() - srv.setupConn(p1, test.flags, test.dialDest) + srv.SetupConn(p1, test.flags, test.dialDest) if !reflect.DeepEqual(test.tt.closeErr, test.wantCloseErr) { t.Errorf("test %d: close error mismatch: got %q, want %q", i, test.tt.closeErr, test.wantCloseErr) } diff --git a/p2p/simulations/examples/connectivity.go b/p2p/simulations/examples/connectivity.go index 43beff951a..472054cb7a 100644 --- a/p2p/simulations/examples/connectivity.go +++ b/p2p/simulations/examples/connectivity.go @@ -99,7 +99,7 @@ func (p *pingPongService) APIs() []rpc.API { return nil } -func (p *pingPongService) Start(server p2p.Server) error { +func (p *pingPongService) Start(server *p2p.Server) error { p.log.Info("ping-pong service starting") return nil } diff --git a/p2p/simulations/http_test.go b/p2p/simulations/http_test.go index f2c31d48e7..99fb49ed8f 100644 --- a/p2p/simulations/http_test.go +++ b/p2p/simulations/http_test.go @@ -46,7 +46,7 @@ func (t *testService) APIs() []rpc.API { }} } -func (t *testService) Start(server p2p.Server) error { +func (t *testService) Start(server *p2p.Server) error { return nil } diff --git a/p2p/testing/protocolsession.go b/p2p/testing/protocolsession.go index d611cd45a5..2de849885d 100644 --- a/p2p/testing/protocolsession.go +++ b/p2p/testing/protocolsession.go @@ -13,8 +13,7 @@ import ( ) type ProtocolSession struct { - p2p.Server - + Server *p2p.Server Ids []*adapters.NodeId adapter *adapters.SimAdapter events chan *p2p.PeerEvent diff --git a/p2p/testing/protocoltester.go b/p2p/testing/protocoltester.go index 695cfd0292..3b104bd12f 100644 --- a/p2p/testing/protocoltester.go +++ b/p2p/testing/protocoltester.go @@ -62,6 +62,10 @@ func NewProtocolTester(t *testing.T, id *adapters.NodeId, n int, run func(*p2p.P return self } +func (self *ProtocolTester) Stop() error { + return self.Server.Stop() +} + func (self *ProtocolTester) Connect(selfId *adapters.NodeId, peers ...*adapters.NodeConfig) { for _, peer := range peers { log.Trace(fmt.Sprintf("start node %v", peer.Id)) @@ -96,7 +100,7 @@ func (t *testNode) APIs() []rpc.API { return nil } -func (t *testNode) Start(server p2p.Server) error { +func (t *testNode) Start(server *p2p.Server) error { return nil } diff --git a/swarm/network/hive.go b/swarm/network/hive.go index 07c1bef6b3..8a9c3d950a 100644 --- a/swarm/network/hive.go +++ b/swarm/network/hive.go @@ -106,7 +106,7 @@ func NewHive(params *HiveParams, overlay Overlay, store Store) *Hive { // these are called on the p2p.Server which runs on the node // af() returns an arbitrary ticker channel // rw is a read writer for json configs -func (self *Hive) Start(server p2p.Server) error { +func (self *Hive) Start(server *p2p.Server) error { if self.store != nil { if err := self.loadPeers(); err != nil { return err diff --git a/swarm/network/hive_test.go b/swarm/network/hive_test.go index 473ba81c0d..6eaa48c48e 100644 --- a/swarm/network/hive_test.go +++ b/swarm/network/hive_test.go @@ -64,7 +64,7 @@ func TestRegisterAndConnect(t *testing.T) { ticker: make(chan time.Time), } pp.newTicker = func() hiveTicker { return tc } - pp.Start(s) + pp.Start(s.Server) defer pp.Stop() tc.ticker <- time.Now() diff --git a/swarm/network/protocol.go b/swarm/network/protocol.go index e4ad09f705..ac24790721 100644 --- a/swarm/network/protocol.go +++ b/swarm/network/protocol.go @@ -164,7 +164,7 @@ func (b *Bzz) APIs() []rpc.API { }} } -func (b *Bzz) Start(server p2p.Server) error { +func (b *Bzz) Start(server *p2p.Server) error { return b.Hive.Start(server) } diff --git a/swarm/network/pss_test.go b/swarm/network/pss_test.go index 502558b0a0..2fc7ddc11b 100644 --- a/swarm/network/pss_test.go +++ b/swarm/network/pss_test.go @@ -89,7 +89,7 @@ func newPssTestService(t *testing.T, handlefunc func(interface{}) error, testnod } } -func (self *pssTestService) Start(server p2p.Server) error { +func (self *pssTestService) Start(server *p2p.Server) error { return self.node.Hive.Start(server) } diff --git a/swarm/network/simulations/overlay.go b/swarm/network/simulations/overlay.go index 1fe613676b..3498be5bca 100644 --- a/swarm/network/simulations/overlay.go +++ b/swarm/network/simulations/overlay.go @@ -64,7 +64,7 @@ func af() <-chan time.Time { // Start() starts up the hive // makes SimNode implement node.Service -func (self *SimNode) Start(server p2p.Server) error { +func (self *SimNode) Start(server *p2p.Server) error { self.init() return self.hive.Start(server, af, self.rw) } diff --git a/swarm/swarm.go b/swarm/swarm.go index a8b6353dd9..3bad8b8e1e 100644 --- a/swarm/swarm.go +++ b/swarm/swarm.go @@ -161,7 +161,7 @@ Start is called when the stack is started * TODO: start subservices like sword, swear, swarmdns */ // implements the node.Service interface -func (self *Swarm) Start(net p2p.Server) error { +func (self *Swarm) Start(net *p2p.Server) error { // set chequebook if self.swapEnabled { ctx := context.Background() // The initial setup has no deadline. diff --git a/whisper/whisperv2/main.go b/whisper/whisperv2/main.go index 081e4fe438..a2e49659bc 100644 --- a/whisper/whisperv2/main.go +++ b/whisper/whisperv2/main.go @@ -48,14 +48,16 @@ func main() { shh := whisper.New() // Create an Ethereum peer to communicate through - server := p2p.NewServer(p2p.Config{ - PrivateKey: key, - MaxPeers: 10, - Name: name, - Protocols: []p2p.Protocol{shh.Protocol()}, - ListenAddr: ":30300", - NAT: nat.Any(), - }) + server := &p2p.Server{ + Config: p2p.Config{ + PrivateKey: key, + MaxPeers: 10, + Name: name, + Protocols: []p2p.Protocol{shh.Protocol()}, + ListenAddr: ":30300", + NAT: nat.Any(), + }, + } fmt.Println("Starting Ethereum peer...") if err := server.Start(); err != nil { fmt.Printf("Failed to start Ethereum peer: %v.\n", err) diff --git a/whisper/whisperv2/whisper.go b/whisper/whisperv2/whisper.go index 6d460bdee5..1d7c21bd12 100644 --- a/whisper/whisperv2/whisper.go +++ b/whisper/whisperv2/whisper.go @@ -172,7 +172,7 @@ func (self *Whisper) Send(envelope *Envelope) error { // Start implements node.Service, starting the background data propagation thread // of the Whisper protocol. -func (self *Whisper) Start(p2p.Server) error { +func (self *Whisper) Start(*p2p.Server) error { log.Info(fmt.Sprint("Whisper started")) go self.update() return nil diff --git a/whisper/whisperv5/peer_test.go b/whisper/whisperv5/peer_test.go index 9e3b66c63d..d3cd63b0b2 100644 --- a/whisper/whisperv5/peer_test.go +++ b/whisper/whisperv5/peer_test.go @@ -78,7 +78,7 @@ type TestData struct { type TestNode struct { shh *Whisper id *ecdsa.PrivateKey - server p2p.Server + server *p2p.Server filerId string } @@ -140,17 +140,19 @@ func initialize(t *testing.T) { peers = append(peers, peer) } - node.server = p2p.NewServer(p2p.Config{ - PrivateKey: node.id, - MaxPeers: NumNodes/2 + 1, - Name: name, - Protocols: node.shh.Protocols(), - ListenAddr: addr, - NAT: nat.Any(), - BootstrapNodes: peers, - StaticNodes: peers, - TrustedNodes: peers, - }) + node.server = &p2p.Server{ + Config: p2p.Config{ + PrivateKey: node.id, + MaxPeers: NumNodes/2 + 1, + Name: name, + Protocols: node.shh.Protocols(), + ListenAddr: addr, + NAT: nat.Any(), + BootstrapNodes: peers, + StaticNodes: peers, + TrustedNodes: peers, + }, + } err = node.server.Start() if err != nil { diff --git a/whisper/whisperv5/whisper.go b/whisper/whisperv5/whisper.go index 77ca20a58c..f2aad08efb 100644 --- a/whisper/whisperv5/whisper.go +++ b/whisper/whisperv5/whisper.go @@ -396,7 +396,7 @@ func (w *Whisper) Send(envelope *Envelope) error { // Start implements node.Service, starting the background data propagation thread // of the Whisper protocol. -func (w *Whisper) Start(p2p.Server) error { +func (w *Whisper) Start(*p2p.Server) error { log.Info("started whisper v." + ProtocolVersionStr) go w.update() From b1555c5e1be1397f13fcf258236e3db1bc333e68 Mon Sep 17 00:00:00 2001 From: Lewis Marshall Date: Sun, 14 May 2017 18:04:56 -0700 Subject: [PATCH 05/24] swarm/network: Fix Kademlia.String() Signed-off-by: Lewis Marshall --- swarm/network/kademlia.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/swarm/network/kademlia.go b/swarm/network/kademlia.go index 72dc7c9ebf..e553b29e6d 100644 --- a/swarm/network/kademlia.go +++ b/swarm/network/kademlia.go @@ -387,7 +387,7 @@ func (self *Kademlia) String() string { var rows []string rows = append(rows, "=========================================================================") - rows = append(rows, fmt.Sprintf("%v KΛÐΞMLIΛ hive: queen's address: %v", time.Now().UTC().Format(time.UnixDate), self)) + rows = append(rows, fmt.Sprintf("%v KΛÐΞMLIΛ hive: queen's address: %v", time.Now().UTC().Format(time.UnixDate), self.BaseAddr())) rows = append(rows, fmt.Sprintf("population: %d (%d), MinProxBinSize: %d, MinBinSize: %d, MaxBinSize: %d", self.conns.Size(), self.addrs.Size(), self.MinProxBinSize, self.MinBinSize, self.MaxBinSize)) liverows := make([]string, self.MaxProxDisplay) From a03781dfb0a3aec731efe5d047c67a426378b581 Mon Sep 17 00:00:00 2001 From: zelig Date: Sun, 14 May 2017 18:42:57 -0700 Subject: [PATCH 06/24] swarm/network: refactor, fix kademlia --- pot/address.go | 6 +- swarm/network/discovery.go | 32 +++++----- swarm/network/discovery_test.go | 2 +- swarm/network/hive.go | 63 ++++++++----------- swarm/network/hive_test.go | 5 -- swarm/network/kademlia.go | 62 +++++++++++-------- swarm/network/kademlia_test.go | 105 +++++++++++++++++++------------- swarm/network/protocol.go | 55 +++++++++-------- swarm/network/protocol_test.go | 70 ++------------------- swarm/network/pss_test.go | 4 +- 10 files changed, 182 insertions(+), 222 deletions(-) diff --git a/pot/address.go b/pot/address.go index e638a11c7d..05f2f82167 100644 --- a/pot/address.go +++ b/pot/address.go @@ -289,7 +289,7 @@ func (self *BoolAddress) PO(val PotVal, pos int) (po int, eq bool) { } type BytesAddress interface { - Bytes() []byte + Address() []byte } type bytesAddress struct { @@ -312,7 +312,7 @@ func ToBytes(v AnyVal) []byte { if !ok { panic(fmt.Sprintf("unsupported value type %T", v)) } - b = ba.Bytes() + b = ba.Address() } return b } @@ -320,7 +320,7 @@ func ToBytes(v AnyVal) []byte { func (a *bytesAddress) String() string { return fmt.Sprintf("%08b", a.bytes) } -func (a *bytesAddress) Bytes() []byte { +func (a *bytesAddress) Address() []byte { return a.bytes } diff --git a/swarm/network/discovery.go b/swarm/network/discovery.go index b1830118d7..80e8e27c70 100644 --- a/swarm/network/discovery.go +++ b/swarm/network/discovery.go @@ -12,12 +12,11 @@ type discPeer struct { *bzzPeer overlay Overlay peers map[string]bool - proxLimit uint8 // the proximity radius advertised by remote to subscribe to peers + depth uint8 // the proximity radius advertised by remote to subscribe to peers sentPeers bool // set to true when the peer is first notifed of peers close to them } -// discovery peer contructor -// registers the handlers for discovery messages +// NewDiscovery discovery peer contructor func NewDiscovery(p *bzzPeer, o Overlay) *discPeer { self := &discPeer{ overlay: o, @@ -47,25 +46,24 @@ func (self *discPeer) HandleMsg(msg interface{}) error { // NotifyPeer notifies the receiver remote end of a peer p or PO po. // callback for overlay driver -func (self *discPeer) NotifyPeer(p OverlayPeer, po uint8) error { - log.Warn(fmt.Sprintf("peer %#v peers %v", p, self.peers)) - if po < self.proxLimit || self.seen(p) { +func (self *discPeer) NotifyPeer(a OverlayAddr, po uint8) error { + if po < self.depth || self.seen(a) { return nil } - log.Warn(fmt.Sprintf("notification about %x", p.Address())) + log.Warn(fmt.Sprintf("notification about %x", a.Address())) resp := &peersMsg{ - Peers: []*bzzAddr{ToAddr(p)}, // perhaps the PeerAddr interface is unnecessary generalization + Peers: []*bzzAddr{ToAddr(a)}, // perhaps the PeerAddr interface is unnecessary generalization } return self.Send(resp) } -// NotifyProx sends a subPeers Msg to the receiver notifying them about +// NotifyDepth sends a subPeers Msg to the receiver notifying them about // a change in the prox limit (radius of the set including the nearest X peers // or first empty row) // callback for overlay driver -func (self *discPeer) NotifyProx(po uint8) error { - return self.Send(&subPeersMsg{ProxLimit: po}) +func (self *discPeer) NotifyDepth(po uint8) error { + return self.Send(&subPeersMsg{Depth: po}) } /* @@ -107,28 +105,28 @@ func (self getPeersMsg) String() string { // subPeers msg is communicating the depth/sharpness/focus of the overlay table of a peer type subPeersMsg struct { - ProxLimit uint8 + Depth uint8 } func (self subPeersMsg) String() string { - return fmt.Sprintf("%T: request peers > PO%02d. ", self, self.ProxLimit) + return fmt.Sprintf("%T: request peers > PO%02d. ", self, self.Depth) } func (self *discPeer) handleSubPeersMsg(msg *subPeersMsg) error { - self.proxLimit = msg.ProxLimit + self.depth = msg.Depth if !self.sentPeers { var peers []*bzzAddr self.overlay.EachConn(self.Over(), 255, func(p OverlayConn, po int, isproxbin bool) bool { - if uint8(po) < self.proxLimit { + if uint8(po) < self.depth { return false } - log.Warn(fmt.Sprintf("peer %#v proxlimit %v", p, self.proxLimit)) + log.Warn(fmt.Sprintf("peer %#v depth %v", p, self.depth)) if !self.seen(p) { peers = append(peers, ToAddr(p)) } return true }) - log.Warn(fmt.Sprintf("found initial %v peers not farther than %v", len(peers), self.proxLimit)) + log.Warn(fmt.Sprintf("found initial %v peers not farther than %v", len(peers), self.depth)) if len(peers) > 0 { if err := self.Send(&peersMsg{Peers: peers}); err != nil { return err diff --git a/swarm/network/discovery_test.go b/swarm/network/discovery_test.go index dfab1eef5a..91a280e0df 100644 --- a/swarm/network/discovery_test.go +++ b/swarm/network/discovery_test.go @@ -33,7 +33,7 @@ func TestDiscovery(t *testing.T) { Expects: []p2ptest.Expect{ p2ptest.Expect{ Code: 3, - Msg: &subPeersMsg{ProxLimit: 0}, + Msg: &subPeersMsg{Depth: 0}, Peer: s.ProtocolTester.Ids[0], }, }, diff --git a/swarm/network/hive.go b/swarm/network/hive.go index 8a9c3d950a..dfa356214b 100644 --- a/swarm/network/hive.go +++ b/swarm/network/hive.go @@ -45,7 +45,7 @@ to keep the nodetable uptodate type Overlay interface { Register(chan OverlayAddr) error - On(OverlayPeer) + On(OverlayConn) Off(OverlayConn) EachConn([]byte, int, func(OverlayConn, int, bool) bool) @@ -57,21 +57,6 @@ type Overlay interface { BaseAddr() []byte } -// Hive implements the PeerPool interface -type Hive struct { - *HiveParams // settings - Overlay // the overlay topology driver - store Store - - // bookkeeping - lock sync.Mutex - quit chan bool - toggle chan bool - more chan bool - - newTicker func() hiveTicker -} - // HiveParams holds the config options to hive type HiveParams struct { Discovery bool // if want discovery of not @@ -90,6 +75,21 @@ func NewHiveParams() *HiveParams { } } +// Hive implements the PeerPool interface +type Hive struct { + *HiveParams // settings + Overlay // the overlay topology driver + store Store + + // bookkeeping + lock sync.Mutex + quit chan bool + toggle chan bool + more chan bool + + newTicker func() hiveTicker +} + // Hive constructor embeds both arguments // HiveParams: config parameters // Overlay: Topology Driver Interface @@ -167,36 +167,27 @@ func (self *Hive) Stop() { close(self.quit) } -func (self *Hive) Run(peer *bzzPeer) error { - discPeer := NewDiscovery(peer, self) - self.On(discPeer) - defer self.Off(discPeer) - return peer.Run(discPeer.HandleMsg) -} - -// Add is called at the end of a successful protocol handshake -// to register a connected (live) peer -func (self *Hive) Add(p *bzzPeer) error { - defer self.wake() +func (self *Hive) Run(p *bzzPeer) error { dp := NewDiscovery(p, self.Overlay) log.Debug(fmt.Sprintf("to add new bee %v", p)) self.On(dp) - self.String() - log.Debug(fmt.Sprintf("%v", self)) - return nil + self.wake() + defer self.wake() + defer self.Off(dp) + return p.Run(dp.HandleMsg) } // Remove called after peer is disconnected -func (self *Hive) Remove(p *bzzPeer) { - defer self.wake() - log.Debug(fmt.Sprintf("remove bee %v", p)) - self.Off(p) -} +// func (self *Hive) Remove(p *bzzPeer) { +// defer self.wake() +// log.Debug(fmt.Sprintf("remove bee %v", p)) +// self.Off(p) +// } // NodeInfo function is used by the p2p.server RPC interface to display // protocol specific node information func (self *Hive) NodeInfo() interface{} { - return interface{}(self.String()) + return self.String() } // PeerInfo function is used by the p2p.server RPC interface to display diff --git a/swarm/network/hive_test.go b/swarm/network/hive_test.go index 6eaa48c48e..8cd11b13a6 100644 --- a/swarm/network/hive_test.go +++ b/swarm/network/hive_test.go @@ -67,11 +67,6 @@ func TestRegisterAndConnect(t *testing.T) { pp.Start(s.Server) defer pp.Stop() tc.ticker <- time.Now() - - // if pp.Overlay.(*testOverlay).posMap[string(raddr.Over())] == nil { - // t.Fatalf("Overlay#On not called on new peer") - // } - // retrieve and broadcast ord := raddr.Over()[0] / 32 o := 0 diff --git a/swarm/network/kademlia.go b/swarm/network/kademlia.go index e553b29e6d..e3d4604597 100644 --- a/swarm/network/kademlia.go +++ b/swarm/network/kademlia.go @@ -98,7 +98,7 @@ func NewKademlia(addr []byte, params *KadParams) *Kademlia { } type Notifier interface { - NotifyPeer(OverlayConn, uint8) error + NotifyPeer(OverlayAddr, uint8) error NotifyDepth(uint8) error } @@ -117,7 +117,6 @@ type OverlayConn interface { type OverlayAddr interface { OverlayPeer - On(OverlayConn) OverlayConn // call to return the connected peer Update(OverlayAddr) OverlayAddr // returns the updated version of the original } @@ -140,18 +139,24 @@ func newEntry(p OverlayPeer) *entry { } } +func (self *entry) addr() OverlayAddr { + a, _ := self.OverlayPeer.(OverlayAddr) + return a +} + +func (self *entry) conn() OverlayConn { + c, _ := self.OverlayPeer.(OverlayConn) + return c +} + func (self *entry) String() string { - return fmt.Sprintf("%x", self.Address()) + return fmt.Sprintf("%x", self.OverlayPeer.Address()) } // Register enters each OverlayAddr as kademlia peer record into the // database of known peer addresses func (self *Kademlia) Register(peers chan OverlayAddr) error { - if len(peers) == 0 { - return fmt.Errorf("empty peers list") - } np := pot.NewPot(nil, 0) - defer func() { self.addrs.Merge(np) }() for p := range peers { // error if self received, peer should know better if bytes.Equal(p.Address(), self.base) { @@ -159,6 +164,9 @@ func (self *Kademlia) Register(peers chan OverlayAddr) error { } np, _, _ = pot.Add(np, pot.PotVal(newEntry(p))) } + com := self.addrs.Merge(np) + log.Trace(fmt.Sprintf("merged %v peers, %v known", np.Size(), com)) + // TODO: remove this check m := make(map[string]bool) self.addrs.Each(func(val pot.PotVal, i int) bool { @@ -186,14 +194,15 @@ func (self *Kademlia) SuggestPeer() (a OverlayAddr, o int, want bool) { ba := pot.NewBytesVal(self.base, nil) self.addrs.EachNeighbour(ba, func(val pot.PotVal, po int) bool { a = self.callable(val) + log.Trace(fmt.Sprintf("candidate prox peer at %x: %v (%v). a == nil is %v", val.(*entry).Address(), a, po, a == nil)) ppo = po - return a != nil && po >= depth + return a == nil && po >= depth }) if a != nil { log.Trace(fmt.Sprintf("candidate prox peer found: %v (%v)", a, ppo)) return a, 0, false } - log.Trace(fmt.Sprintf("no candidate prox peers to connect to (Depth: %v, minProxSize: %v)", depth, self.MinProxBinSize)) + log.Trace(fmt.Sprintf("no candidate prox peers to connect to (Depth: %v, minProxSize: %v) %#v", depth, self.MinProxBinSize, a)) var bpo []int prev := -1 @@ -225,7 +234,7 @@ func (self *Kademlia) SuggestPeer() (a OverlayAddr, o int, want bool) { log.Trace(fmt.Sprintf("check PO%02d: ", po)) f(func(val pot.PotVal, j int) bool { a = self.callable(val) - return a != nil && po < depth + return a == nil && po < depth }) return false }) @@ -241,7 +250,7 @@ func (self *Kademlia) SuggestPeer() (a OverlayAddr, o int, want bool) { } // On inserts the peer as a kademlia peer into the live peers -func (self *Kademlia) On(p OverlayPeer) { +func (self *Kademlia) On(p OverlayConn) { e := newEntry(p) self.conns.Swap(p, func(v pot.PotVal) pot.PotVal { // if not found live @@ -257,10 +266,12 @@ func (self *Kademlia) On(p OverlayPeer) { return v }) + log.Trace(fmt.Sprintf("Notifier:%#v", p)) np, ok := p.(Notifier) if !ok { return } + log.Trace(fmt.Sprintf("notify:%v", p)) depth := uint8(self.Depth()) if depth != self.depth { @@ -271,12 +282,12 @@ func (self *Kademlia) On(p OverlayPeer) { go np.NotifyDepth(depth) f := func(val pot.PotVal, po int) { - dp := val.(Notifier) - dp.NotifyPeer(p.(OverlayConn), uint8(po)) + dp := val.(*entry).OverlayPeer.(Notifier) + dp.NotifyPeer(p.Off(), uint8(po)) log.Trace(fmt.Sprintf("peer %v notified of %v (%v)", dp, p, po)) if depth > 0 { dp.NotifyDepth(depth) - log.Trace("peer %v notified of new depth limit %v", dp, depth) + log.Trace("peer %v notified of new depth %v", dp, depth) } } self.conns.EachNeighbourAsync(e, 1024, 255, f, false) @@ -290,10 +301,10 @@ func (self *Kademlia) Off(p OverlayConn) { panic(fmt.Sprintf("connected peer not found %v", p)) } self.conns.Swap(p, func(v pot.PotVal) pot.PotVal { - // v cannot nil, but no need to check + // v cannot be nil, but no need to check return nil }) - return newEntry(p) + return newEntry(p.Off()) }) } @@ -313,7 +324,7 @@ func (self *Kademlia) EachConn(base []byte, o int, f func(OverlayConn, int, bool if l, _ := p.PO(val, 0); l >= self.Depth() { isproxbin = true } - return f(val.(OverlayConn), po, isproxbin) + return f(val.(*entry).conn(), po, isproxbin) }) } @@ -329,7 +340,7 @@ func (self *Kademlia) EachAddr(base []byte, o int, f func(OverlayAddr, int) bool if po > o { return true } - return f(val.(OverlayAddr), po) + return f(val.(*entry).addr(), po) }) } @@ -353,7 +364,7 @@ func (self *Kademlia) Depth() (depth int) { func (self *Kademlia) callable(val pot.PotVal) OverlayAddr { e := val.(*entry) // not callable if peer is live or exceeded maxRetries - if _, live := val.(OverlayConn); live || e.retries > self.MaxRetries { + if e.conn() != nil || e.retries > self.MaxRetries { log.Trace(fmt.Sprintf("peer %v (%T) not callable", e, e.OverlayPeer)) return nil } @@ -368,26 +379,27 @@ func (self *Kademlia) callable(val pot.PotVal) OverlayAddr { // this is never called concurrently, so safe to increment // peer can be retried again if retries < e.retries { - log.Trace(fmt.Sprintf("log time needed before retry %v, wait only warrants %v", e.retries, retries)) + log.Trace(fmt.Sprintf("long time since last try (at %v) needed before retry %v, wait only warrants %v", timeAgo, e.retries, retries)) return nil } e.retries++ log.Trace(fmt.Sprintf("peer %v is callable", e)) - return val.(OverlayAddr) + return e.addr() } +// BaseAddr return the kademlia base addres func (self *Kademlia) BaseAddr() []byte { return self.base } -// kademlia table + kaddb table displayed with ascii +// String returns kademlia table + kaddb table displayed with ascii func (self *Kademlia) String() string { var rows []string rows = append(rows, "=========================================================================") - rows = append(rows, fmt.Sprintf("%v KΛÐΞMLIΛ hive: queen's address: %v", time.Now().UTC().Format(time.UnixDate), self.BaseAddr())) + rows = append(rows, fmt.Sprintf("%v KΛÐΞMLIΛ hive: queen's address: %x", time.Now().UTC().Format(time.UnixDate), self.BaseAddr()[:3])) rows = append(rows, fmt.Sprintf("population: %d (%d), MinProxBinSize: %d, MinBinSize: %d, MaxBinSize: %d", self.conns.Size(), self.addrs.Size(), self.MinProxBinSize, self.MinBinSize, self.MaxBinSize)) liverows := make([]string, self.MaxProxDisplay) @@ -468,12 +480,12 @@ func (self *Kademlia) Prune(c <-chan time.Time) { go func() { for range c { total := 0 - self.conns.EachBin(nil, 0, func(po, size int, f func(func(pot.PotVal, int) bool) bool) bool { + self.conns.EachBin(pot.NewBytesVal(self.base, nil), 0, func(po, size int, f func(func(pot.PotVal, int) bool) bool) bool { extra := size - self.MinBinSize if size > self.MaxBinSize { n := 0 f(func(v pot.PotVal, po int) bool { - v.(OverlayConn).Drop(fmt.Errorf("bucket full")) + v.(*entry).conn().Drop(fmt.Errorf("bucket full")) n++ return n < extra }) diff --git a/swarm/network/kademlia_test.go b/swarm/network/kademlia_test.go index c6bc875976..1d1ebfba17 100644 --- a/swarm/network/kademlia_test.go +++ b/swarm/network/kademlia_test.go @@ -17,13 +17,21 @@ package network import ( "fmt" + "os" "sync" "testing" "time" + "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/pot" ) +func init() { + h := log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(true))) + // h := log.CallerFileHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(true))) + log.Root().SetHandler(h) +} + func testKadPeerAddr(s string) *bzzAddr { a := pot.NewHashAddress(s).Bytes() return &bzzAddr{OAddr: a, UAddr: a} @@ -57,23 +65,24 @@ type dropError struct { } func (self *testDropPeer) Drop(err error) { - err2 := &dropError{err, overlayStr(self)} + err2 := &dropError{err, binStr(self)} self.dropc <- err2 } -func (self *testDiscPeer) NotifyProx(po uint8) error { - key := overlayStr(self) +func (self *testDiscPeer) NotifyDepth(po uint8) error { + key := binStr(self) self.lock.Lock() defer self.lock.Unlock() self.notifications[key] = po return nil } -func (self *testDiscPeer) NotifyPeer(p OverlayPeer, po uint8) error { - key := overlayStr(self) - key += overlayStr(p) +func (self *testDiscPeer) NotifyPeer(p OverlayAddr, po uint8) error { + key := binStr(self) + key += binStr(p) self.lock.Lock() defer self.lock.Unlock() + log.Trace(fmt.Sprintf("key %v=>%v", key, po)) self.notifications[key] = po return nil } @@ -108,34 +117,6 @@ func (k *testKademlia) newTestKadPeer(s string) Peer { return Peer(dp) } -func overlayStr(a OverlayPeer) string { - // log.Error(fmt.Sprintf("PeerAddr: %v (%T)", a, a)) - // if a == (*KadPeer)(nil) || a == (*testDiscPeer)(nil) || a == (*bzzPeer)(nil) || a == nil { - // return "" - // } - // var p Peer - // s, ok := a.(*KadPeer) - // if ok { - // p = s.Peer - // } else { - // p = a.(*testDiscPeer).Peer - // } - // log.Error(fmt.Sprintf("PeerAddr: %v (%T)", p, p)) - // if p == (Peer)(nil) || p == (*testDiscPeer)(nil) || p == (*bzzPeer)(nil) { - // return "" - // } - // return pot.NewHashAddressFromBytes(p.OverlayAddr()).Bin()[:6] - // if a == nil { - // return "" - // } - // k, ok := a.(*KadPeer) - // if ok && k.Peer != nil { - // return pot.ToBin(a.(*KadPeer).Peer.Over())[:6] - // } - // return pot.ToBin(a.Over())[:6] - return pot.ToBin(a.Address()) -} - func (k *testKademlia) On(ons ...string) *testKademlia { for _, s := range ons { p := k.newTestKadPeer(s) @@ -160,14 +141,16 @@ func (k *testKademlia) Register(regs ...string) *testKademlia { ch <- testKadPeerAddr(s) } }() - k.Kademlia.Register(ch) + err := k.Kademlia.Register(ch) + log.Trace(fmt.Sprintf("register %v addresses: %v", len(regs), err)) + return k } func testSuggestPeer(t *testing.T, k *testKademlia, expAddr string, expPo int, expWant bool) error { addr, o, want := k.SuggestPeer() - if overlayStr(addr) != expAddr { - return fmt.Errorf("incorrect peer address suggested. expected %v, got %v", expAddr, overlayStr(addr)) + if binStr(addr) != expAddr { + return fmt.Errorf("incorrect peer address suggested. expected %v, got %v", expAddr, binStr(addr)) } if o != expPo { return fmt.Errorf("incorrect prox order suggested. expected %v, got %v", expPo, o) @@ -178,6 +161,13 @@ func testSuggestPeer(t *testing.T, k *testKademlia, expAddr string, expPo int, e return nil } +func binStr(a OverlayPeer) string { + if a == nil { + return "" + } + return pot.ToBin(a.Address())[:6] +} + func TestSuggestPeerFindPeers(t *testing.T) { // 2 row gap, unsaturated proxbin, no callables -> want PO 0 k := newTestKademlia("000000").On("001000") @@ -225,7 +215,6 @@ func TestSuggestPeerFindPeers(t *testing.T) { // second time disconnected peer not callable // with reasonably set Interval - // err = testSuggestPeer(t, k, "010000", 2, true) err = testSuggestPeer(t, k, "", 1, true) if err != nil { t.Fatal(err.Error()) @@ -240,16 +229,16 @@ func TestSuggestPeerFindPeers(t *testing.T) { } k.On("010000") - k.Off("010000") - // PO1 disconnects // new closer peer appears, it is immediately wanted - // k.Off("010000") k.Register("000101") err = testSuggestPeer(t, k, "000101", 0, false) if err != nil { t.Fatal(err.Error()) } + // PO1 disconnects + k.On("000101") + k.Off("010000") // second time, gap filling err = testSuggestPeer(t, k, "010000", 0, false) if err != nil { @@ -268,6 +257,19 @@ func TestSuggestPeerFindPeers(t *testing.T) { t.Fatal(err.Error()) } + k.Register("010001") + err = testSuggestPeer(t, k, "", 0, true) + if err != nil { + t.Fatal(err.Error()) + } + + k.On("100001") + log.Trace("Kad:\n%v", k.String()) + err = testSuggestPeer(t, k, "010001", 0, false) + if err != nil { + t.Fatal(err.Error()) + } + k.On("100001") k.On("010001") err = testSuggestPeer(t, k, "", 0, false) @@ -276,7 +278,18 @@ func TestSuggestPeerFindPeers(t *testing.T) { } k.MinBinSize = 3 + k.Register("100010") + err = testSuggestPeer(t, k, "100010", 0, false) + if err != nil { + t.Fatal(err.Error()) + } + k.On("100010") + err = testSuggestPeer(t, k, "", 1, true) + if err != nil { + t.Fatal(err.Error()) + } + k.On("010010") err = testSuggestPeer(t, k, "", 2, true) if err != nil { @@ -284,8 +297,16 @@ func TestSuggestPeerFindPeers(t *testing.T) { } k.On("001010") + err = testSuggestPeer(t, k, "", 3, true) + if err != nil { + log.Trace("Kad:\n%v", k.String()) + t.Fatal(err.Error()) + } + + k.On("000110") err = testSuggestPeer(t, k, "", 0, false) if err != nil { + log.Trace("Kad:\n%v", k.String()) t.Fatal(err.Error()) } @@ -443,7 +464,7 @@ func TestNotifications(t *testing.T) { k.Discovery = true k.MinProxBinSize = 3 k.On("010000", "001000") - time.Sleep(100 * time.Millisecond) + time.Sleep(1000 * time.Millisecond) err := k.checkNotifications( []*testPeerNotification{ &testPeerNotification{"010000", "001000", 1}, diff --git a/swarm/network/protocol.go b/swarm/network/protocol.go index ac24790721..fd42276589 100644 --- a/swarm/network/protocol.go +++ b/swarm/network/protocol.go @@ -88,6 +88,7 @@ type Conn interface { Send(interface{}) error // can send messages Drop(error) // disconnect this peer Run(func(interface{}) error) error // the run function to run a protocol + Off() OverlayAddr } // TODO: implement store for exec nodes @@ -107,6 +108,18 @@ type BzzConfig struct { Store Store } +// Bzz is the swarm protocol bundle +type Bzz struct { + Kademlia *Kademlia + Hive *Hive + Pss *Pss + + localAddr *bzzAddr + mtx sync.Mutex + handshakes map[discover.NodeID]*bzzHandshake +} + +// NewBzz is the swarm protocol constructor func NewBzz(config *BzzConfig) *Bzz { kademlia := NewKademlia(config.OverlayAddr, config.KadParams) bzz := &Bzz{ @@ -121,16 +134,10 @@ func NewBzz(config *BzzConfig) *Bzz { return bzz } -type Bzz struct { - Kademlia *Kademlia - Hive *Hive - Pss *Pss - - localAddr *bzzAddr - mtx sync.Mutex - handshakes map[discover.NodeID]*bzzHandshake -} - +// Bzz implements the node.Service interface, offers Protocols +// * handshake/hive +// * discovery +// * pss func (b *Bzz) Protocols() []p2p.Protocol { return []p2p.Protocol{ { @@ -156,6 +163,9 @@ func (b *Bzz) Protocols() []p2p.Protocol { } } +// Bzz implements the node.Service interface, offers APIs: +// * hive +// * pss func (b *Bzz) APIs() []rpc.API { return []rpc.API{{ Namespace: "hive", @@ -200,7 +210,7 @@ func (b *Bzz) runProtocol(spec *protocols.Spec, run func(*bzzPeer) error) func(* // the handshake has succeeded so run the service peer := &bzzPeer{ - Conn: protocols.NewPeer(p, rw, spec), + Peer: protocols.NewPeer(p, rw, spec), localAddr: b.localAddr, bzzAddr: handshake.peerAddr, } @@ -227,15 +237,15 @@ func (b *Bzz) getHandshake(peerID discover.NodeID) *bzzHandshake { // bzzPeer is the bzz protocol view of a protocols.Peer (itself an extension of p2p.Peer) // implements the Peer interface and all interfaces Peer implements: Addr, OverlayPeer type bzzPeer struct { - Conn // represents the connection for online peers - localAddr *bzzAddr // local Peers address - *bzzAddr // remote address -> implements Addr interface = protocols.Peer - lastActive time.Time // time is updated whenever mutexes are releasing + *protocols.Peer // represents the connection for online peers + localAddr *bzzAddr // local Peers address + *bzzAddr // remote address -> implements Addr interface = protocols.Peer + lastActive time.Time // time is updated whenever mutexes are releasing } -func newBzzPeer(conn Conn, over, under []byte) *bzzPeer { +func newBzzPeer(p *protocols.Peer, over, under []byte) *bzzPeer { return &bzzPeer{ - Conn: conn, + Peer: p, localAddr: &bzzAddr{over, under}, } } @@ -318,23 +328,16 @@ func (self *bzzAddr) Address() []byte { return self.OAddr } -func (self *bzzAddr) Bytes() []byte { - return self.OAddr -} +// Over returns the overlay address func (self *bzzAddr) Over() []byte { return self.OAddr } +// Under retrun the underlay address func (self *bzzAddr) Under() []byte { return self.UAddr } -func (self *bzzAddr) On(p OverlayConn) OverlayConn { - bp := p.(*bzzPeer) - bp.bzzAddr = self - return bp -} - func (self *bzzAddr) Update(a OverlayAddr) OverlayAddr { return &bzzAddr{self.OAddr, a.(Addr).Under()} } diff --git a/swarm/network/protocol_test.go b/swarm/network/protocol_test.go index 15875eb9ce..6848f49af0 100644 --- a/swarm/network/protocol_test.go +++ b/swarm/network/protocol_test.go @@ -5,7 +5,6 @@ import ( "sync" "testing" - "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/protocols" "github.com/ethereum/go-ethereum/p2p/simulations/adapters" @@ -73,7 +72,7 @@ func newBzzBaseTester(t *testing.T, n int, addr *bzzAddr, spec *protocols.Spec, protocall := func(p *p2p.Peer, rw p2p.MsgReadWriter) error { return srv(&bzzPeer{ - Conn: protocols.NewPeer(p, rw, spec), + Peer: protocols.NewPeer(p, rw, spec), localAddr: addr, bzzAddr: NewAddrFromNodeId(&adapters.NodeId{NodeID: p.ID()}), }) @@ -103,6 +102,9 @@ func newBzzTester(t *testing.T, n int, addr *bzzAddr, pp *p2ptest.TestPeerPool, extraservices := func(p *bzzPeer) error { pp.Add(p) defer pp.Remove(p) + if services == nil { + return nil + } return services(p) } return newBzzBaseTester(t, n, addr, spec, extraservices) @@ -183,67 +185,3 @@ func TestBzzHandshakeSuccess(t *testing.T) { &bzzHandshake{Version: 0, NetworkId: 322, Addr: NewAddrFromNodeId(id)}, ) } - -func TestBzzPeerPoolAdd(t *testing.T) { - pp := p2ptest.NewTestPeerPool() - addr := RandomAddr() - s := newBzzTester(t, 1, addr, pp, nil, nil) - defer s.Stop() - - id := s.Ids[0] - log.Trace(fmt.Sprintf("handshake with %v", id)) - s.runHandshakes() - - if !pp.Has(id) { - t.Fatalf("peer '%v' not added: %v", id, pp) - } -} - -func TestBzzPeerPoolRemove(t *testing.T) { - addr := RandomAddr() - pp := p2ptest.NewTestPeerPool() - s := newBzzTester(t, 1, addr, pp, nil, nil) - defer s.Stop() - - s.runHandshakes() - - id := s.Ids[0] - pp.Get(id).Drop(fmt.Errorf("p2p: read or write on closed message pipe")) - s.TestDisconnected(&p2ptest.Disconnect{id, fmt.Errorf("p2p: read or write on closed message pipe")}) - if pp.Has(id) { - t.Fatalf("peer '%v' not removed: %v", id, pp) - } -} - -func TestBzzPeerPoolBothAddRemove(t *testing.T) { - addr := RandomAddr() - pp := p2ptest.NewTestPeerPool() - s := newBzzTester(t, 1, addr, pp, nil, nil) - defer s.Stop() - - s.runHandshakes() - - id := s.Ids[0] - if !pp.Has(id) { - t.Fatalf("peer '%v' not added: %v", id, pp) - } - - pp.Get(id).Drop(fmt.Errorf("p2p: read or write on closed message pipe")) - s.TestDisconnected(&p2ptest.Disconnect{Peer: id, Error: fmt.Errorf("p2p: read or write on closed message pipe")}) - if pp.Has(id) { - t.Fatalf("peer '%v' not removed: %v", id, pp) - } -} - -func TestBzzPeerPoolNotAdd(t *testing.T) { - addr := RandomAddr() - pp := p2ptest.NewTestPeerPool() - s := newBzzTester(t, 1, addr, pp, nil, nil) - defer s.Stop() - - id := s.Ids[0] - s.testHandshake(correctBzzHandshake(addr), &bzzHandshake{Version: 0, NetworkId: 321, Addr: NewAddrFromNodeId(id)}, &p2ptest.Disconnect{Peer: id, Error: fmt.Errorf("network id mismatch 321 (!= 322)")}) - if pp.Has(id) { - t.Fatalf("peer %v incorrectly added: %v", id, pp) - } -} diff --git a/swarm/network/pss_test.go b/swarm/network/pss_test.go index 2fc7ddc11b..3c059ac972 100644 --- a/swarm/network/pss_test.go +++ b/swarm/network/pss_test.go @@ -12,7 +12,9 @@ const ( ) func init() { - h := log.CallerFileHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(true))) + h := log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(true))) + // + // h := log.CallerFileHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(true))) log.Root().SetHandler(h) } From 5307e1ea771bf8fb49d419d2589da58972180f47 Mon Sep 17 00:00:00 2001 From: zelig Date: Tue, 16 May 2017 15:49:44 -0700 Subject: [PATCH 07/24] swarm/network: Kademlia fixes --- swarm/network/discovery.go | 9 ++- swarm/network/hive.go | 22 +++--- swarm/network/kademlia.go | 16 ++-- swarm/network/protocol.go | 13 +++- swarm/network/simulations/overlay.go | 109 ++++++++++----------------- 5 files changed, 76 insertions(+), 93 deletions(-) diff --git a/swarm/network/discovery.go b/swarm/network/discovery.go index 80e8e27c70..e6492e2b35 100644 --- a/swarm/network/discovery.go +++ b/swarm/network/discovery.go @@ -120,9 +120,8 @@ func (self *discPeer) handleSubPeersMsg(msg *subPeersMsg) error { if uint8(po) < self.depth { return false } - log.Warn(fmt.Sprintf("peer %#v depth %v", p, self.depth)) if !self.seen(p) { - peers = append(peers, ToAddr(p)) + peers = append(peers, ToAddr(p.Off())) } return true }) @@ -147,13 +146,15 @@ func (self *discPeer) handlePeersMsg(msg *peersMsg) error { return nil } - var c chan OverlayAddr + c := make(chan OverlayAddr) go func() { + defer close(c) for _, a := range msg.Peers { self.seen(a) c <- a } }() + log.Info("discovery overlay register") return self.overlay.Register(c) } @@ -168,7 +169,7 @@ func (self *discPeer) handleGetPeersMsg(msg *getPeersMsg) error { self.overlay.EachConn(self.Over(), int(msg.Order), func(p OverlayConn, po int, isproxbin bool) bool { i++ // only send peers we have not sent before in this session - a := ToAddr(p) + a := ToAddr(p.Off()) if self.seen(a) { peers = append(peers, a) } diff --git a/swarm/network/hive.go b/swarm/network/hive.go index dfa356214b..1a361cb86f 100644 --- a/swarm/network/hive.go +++ b/swarm/network/hive.go @@ -126,7 +126,8 @@ func (self *Hive) Start(server *p2p.Server) error { // to attempt to write to more (remove Peer when shutting down) return } - log.Trace("hive delegate to overlay driver: suggest addr to connect to") + log.Debug("hive delegate to overlay driver: suggest addr to connect to") + // log.Trace("hive delegate to overlay driver: suggest addr to connect to") addr, order, want := self.SuggestPeer() if addr != nil { @@ -143,16 +144,17 @@ func (self *Hive) Start(server *p2p.Server) error { want = want && self.Discovery if want { + log.Debug(fmt.Sprintf("========> request peers nearest %v", addr)) RequestOrder(self.Overlay, uint8(order), self.PeersBroadcastSetSize, self.MaxPeersPerRequest) } + log.Info(fmt.Sprintf("%v", self)) select { case self.toggle <- want: log.Trace(fmt.Sprintf("keep hive alive: %v", want)) case <-self.quit: return } - // log.Info(fmt.Sprintf("%v", self)) } }() return nil @@ -177,13 +179,6 @@ func (self *Hive) Run(p *bzzPeer) error { return p.Run(dp.HandleMsg) } -// Remove called after peer is disconnected -// func (self *Hive) Remove(p *bzzPeer) { -// defer self.wake() -// log.Debug(fmt.Sprintf("remove bee %v", p)) -// self.Off(p) -// } - // NodeInfo function is used by the p2p.server RPC interface to display // protocol specific node information func (self *Hive) NodeInfo() interface{} { @@ -229,6 +224,9 @@ func ToAddr(pa OverlayPeer) *bzzAddr { if addr, ok := pa.(*bzzAddr); ok { return addr } + if p, ok := pa.(*discPeer); ok { + return p.bzzAddr + } return pa.(*bzzPeer).bzzAddr } @@ -262,7 +260,7 @@ func (self *Hive) keepAlive() { for { select { case <-tick: - log.Trace("wake up: make hive alive") + log.Debug("wake up: make hive alive") self.wake() case need := <-self.toggle: if ticker == nil && need { @@ -295,9 +293,9 @@ func (self *Hive) loadPeers() error { return err } - var c chan OverlayAddr - defer close(c) + c := make(chan OverlayAddr) go func() { + defer close(c) for _, a := range as { c <- a } diff --git a/swarm/network/kademlia.go b/swarm/network/kademlia.go index e3d4604597..80ecad5962 100644 --- a/swarm/network/kademlia.go +++ b/swarm/network/kademlia.go @@ -162,10 +162,11 @@ func (self *Kademlia) Register(peers chan OverlayAddr) error { if bytes.Equal(p.Address(), self.base) { return fmt.Errorf("add peers: %x is self", self.base) } - np, _, _ = pot.Add(np, pot.PotVal(newEntry(p))) + np, _, _ = pot.Add(np, newEntry(p)) } com := self.addrs.Merge(np) - log.Trace(fmt.Sprintf("merged %v peers, %v known", np.Size(), com)) + log.Debug(fmt.Sprintf("merged %v peers, %v known, total: %v", np.Size(), com, self.addrs.Size())) + // log.Trace(fmt.Sprintf("merged %v peers, %v known", np.Size(), com)) // TODO: remove this check m := make(map[string]bool) @@ -189,7 +190,8 @@ func (self *Kademlia) SuggestPeer() (a OverlayAddr, o int, want bool) { depth := self.Depth() // if there is a callable neighbour within the current proxBin, connect // this makes sure nearest neighbour set is fully connected - log.Trace(fmt.Sprintf("candidate prox peer checking above PO %v", depth)) + log.Debug(fmt.Sprintf("candidate prox peer checking above PO %v", depth)) + // log.Trace(fmt.Sprintf("candidate prox peer checking above PO %v", depth)) var ppo int ba := pot.NewBytesVal(self.base, nil) self.addrs.EachNeighbour(ba, func(val pot.PotVal, po int) bool { @@ -266,12 +268,10 @@ func (self *Kademlia) On(p OverlayConn) { return v }) - log.Trace(fmt.Sprintf("Notifier:%#v", p)) np, ok := p.(Notifier) if !ok { return } - log.Trace(fmt.Sprintf("notify:%v", p)) depth := uint8(self.Depth()) if depth != self.depth { @@ -284,10 +284,12 @@ func (self *Kademlia) On(p OverlayConn) { f := func(val pot.PotVal, po int) { dp := val.(*entry).OverlayPeer.(Notifier) dp.NotifyPeer(p.Off(), uint8(po)) - log.Trace(fmt.Sprintf("peer %v notified of %v (%v)", dp, p, po)) + // log.Trace(fmt.Sprintf("peer %v notified of %v (%v)", dp, p, po)) + log.Debug(fmt.Sprintf("peer %v notified of %v (%v)", dp, p, po)) if depth > 0 { dp.NotifyDepth(depth) - log.Trace("peer %v notified of new depth %v", dp, depth) + log.Debug(fmt.Sprintf("peer %v notified of new depth %v", dp, depth)) + // log.Trace(fmt.Sprintf("peer %v notified of new depth %v", dp, depth)) } } self.conns.EachNeighbourAsync(e, 1024, 255, f, false) diff --git a/swarm/network/protocol.go b/swarm/network/protocol.go index fd42276589..9ef52f3dc4 100644 --- a/swarm/network/protocol.go +++ b/swarm/network/protocol.go @@ -20,6 +20,7 @@ import ( "context" "errors" "fmt" + "net" "sync" "time" @@ -185,6 +186,7 @@ func (b *Bzz) Stop() error { func (b *Bzz) runHandshake(p *p2p.Peer, rw p2p.MsgReadWriter) error { handshake := b.getHandshake(p.ID()) + defer b.removeHandshake(p.ID()) if err := handshake.Perform(p, rw); err != nil { log.Error("handshake failed", "peer", p.ID(), "err", err) @@ -218,6 +220,12 @@ func (b *Bzz) runProtocol(spec *protocols.Spec, run func(*bzzPeer) error) func(* } } +func (b *Bzz) removeHandshake(peerID discover.NodeID) { + b.mtx.Lock() + defer b.mtx.Unlock() + delete(b.handshakes, peerID) +} + func (b *Bzz) getHandshake(peerID discover.NodeID) *bzzHandshake { b.mtx.Lock() defer b.mtx.Unlock() @@ -370,5 +378,8 @@ func NewNodeIdFromAddr(addr Addr) *adapters.NodeId { // the overlay address is derived as the hash of the nodeId func NewAddrFromNodeId(n *adapters.NodeId) *bzzAddr { id := n.NodeID - return &bzzAddr{crypto.Keccak256(id[:]), id[:]} + return &bzzAddr{ + OAddr: crypto.Keccak256(id[:]), + UAddr: []byte(discover.NewNode(id, net.IP{127, 0, 0, 1}, 30303, 30303).String()), + } } diff --git a/swarm/network/simulations/overlay.go b/swarm/network/simulations/overlay.go index 3498be5bca..85ac83d2f5 100644 --- a/swarm/network/simulations/overlay.go +++ b/swarm/network/simulations/overlay.go @@ -11,66 +11,57 @@ import ( "net/http" "os" "runtime" + "sync" "time" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/node" - "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/p2p/discover" "github.com/ethereum/go-ethereum/p2p/simulations" "github.com/ethereum/go-ethereum/p2p/simulations/adapters" - "github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/swarm/network" ) -// SimNode is the adapter used by Swarm simulations. -type SimNode struct { - id *adapters.NodeId - rw network.ReadWriter - hive *network.Hive - protocol *p2p.Protocol -} - -type simReadWriter struct { +type simStore struct { m map[string][]byte } -func (self *simReadWriter) ReadAll(s string) ([]byte, error) { +func (self *simStore) Load(s string) ([]byte, error) { return self.m[s], nil } -func (self *simReadWriter) WriteAll(s string, data []byte) error { +func (self *simStore) Save(s string, data []byte) error { self.m[s] = data return nil } -func NewSimReadWriter() *simReadWriter { - return &simReadWriter{ +func NewSimStore() *simStore { + return &simStore{ make(map[string][]byte), } } -func (s *SimNode) Protocols() []p2p.Protocol { - return []p2p.Protocol{*s.protocol} +type Simulation struct { + mtx sync.Mutex + stores map[discover.NodeID]*simStore } -func (s *SimNode) APIs() []rpc.API { - return nil +func NewSimulation() *Simulation { + return &Simulation{ + stores: make(map[discover.NodeID]*simStore), + } } -// the hive update ticker for hive -func af() <-chan time.Time { - return time.NewTicker(1 * time.Second).C -} +func (s *Simulation) NewService(id *adapters.NodeId, snapshot []byte) node.Service { + s.mtx.Lock() + store, ok := s.stores[id.NodeID] + if !ok { + store = NewSimStore() + s.stores[id.NodeID] = store + } + s.mtx.Unlock() -// Start() starts up the hive -// makes SimNode implement node.Service -func (self *SimNode) Start(server *p2p.Server) error { - self.init() - return self.hive.Start(server, af, self.rw) -} - -func (self *SimNode) init() { - addr := network.NewPeerAddrFromNodeId(self.id) + addr := network.NewAddrFromNodeId(id) kp := network.NewKadParams() kp.MinProxBinSize = 2 @@ -80,42 +71,16 @@ func (self *SimNode) init() { kp.RetryExponent = 2 kp.RetryInterval = 1000000 - to := network.NewKademlia(addr.OverlayAddr(), kp) // overlay topology driver hp := network.NewHiveParams() - hp.CallInterval = 5000 - pp := network.NewHive(hp, to) // hive - - services := func(p network.Peer) error { - dp := network.NewDiscovery(p, to) - pp.Add(dp) - log.Trace(fmt.Sprintf("kademlia on %v", dp)) - p.DisconnectHook(func(err error) { - pp.Remove(dp) - }) - return nil + hp.KeepAliveInterval = 5 * time.Second + config := &network.BzzConfig{ + OverlayAddr: addr.Over(), + UnderlayAddr: addr.Under(), + KadParams: kp, + HiveParams: hp, + Store: store, } - - ct := network.BzzCodeMap(network.DiscoveryMsgs...) // bzz protocol code map - nodeInfo := func() interface{} { return pp.String() } - self.hive = pp - self.protocol = network.Bzz(addr.OverlayAddr(), addr.UnderlayAddr(), ct, services, nil, nodeInfo) -} - -// Stop() shuts down the hive -// makes SimNode implement node.Service -func (self *SimNode) Stop() error { - self.hive.Stop() - return nil -} - -// NewSimNode creates adapters for nodes in the simulation. -func NewSimNode(id *adapters.NodeId, snapshot []byte) node.Service { - s := &SimNode{ - id: id, - rw: NewSimReadWriter(), - } - s.init() - return s + return network.NewBzz(config) } func createMockers() map[string]*simulations.MockerConfig { @@ -171,7 +136,12 @@ func setupMocker(net *simulations.Network) []*adapters.NodeId { } else { peerId = ids[i-1] } - if err := net.Connect(id, peerId); err != nil { + ch := make(chan network.OverlayAddr) + go func() { + defer close(ch) + ch <- network.NewAddrFromNodeId(peerId) + }() + if err := net.GetNode(id).Node.(*adapters.SimNode).Service().(*network.Bzz).Hive.Register(ch); err != nil { panic(err.Error()) } } @@ -243,10 +213,11 @@ func startStopMocker(net *simulations.Network) { func main() { runtime.GOMAXPROCS(runtime.NumCPU()) - log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(false)))) + log.Root().SetHandler(log.LvlFilterHandler(log.LvlDebug, log.StreamHandler(os.Stderr, log.TerminalFormat(false)))) + s := NewSimulation() services := adapters.Services{ - "overlay": NewSimNode, + "overlay": s.NewService, } adapters.RegisterServices(services) From c2efe3c366657225ff730c3e79dcc1bf7a101da4 Mon Sep 17 00:00:00 2001 From: Lewis Marshall Date: Tue, 16 May 2017 21:17:00 -0700 Subject: [PATCH 08/24] swarm/network/simulations: Improve overlay simulation Signed-off-by: Lewis Marshall --- swarm/network/simulations/overlay.go | 37 ++++++++++++++-------------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/swarm/network/simulations/overlay.go b/swarm/network/simulations/overlay.go index 85ac83d2f5..a18efcf03a 100644 --- a/swarm/network/simulations/overlay.go +++ b/swarm/network/simulations/overlay.go @@ -112,8 +112,9 @@ func setupMocker(net *simulations.Network) []*adapters.NodeId { conf := net.Config() conf.DefaultService = "overlay" - ids := make([]*adapters.NodeId, 10) - for i := 0; i < 10; i++ { + nodeCount := 30 + ids := make([]*adapters.NodeId, nodeCount) + for i := 0; i < nodeCount; i++ { node, err := net.NewNode() if err != nil { panic(err.Error()) @@ -189,23 +190,23 @@ func randomMocker(net *simulations.Network) { func startStopMocker(net *simulations.Network) { ids := setupMocker(net) - for i, id := range ids { - n := 3000 + i*1000 - go func(id *adapters.NodeId) { - for { - // n := rand.Intn(5000) - // n := 3000 - time.Sleep(time.Duration(n) * time.Millisecond) - log.Debug(fmt.Sprintf("node %v shutting down", id)) - net.Stop(id) - // n = rand.Intn(5000) - n = 2000 - time.Sleep(time.Duration(n) * time.Millisecond) - log.Debug(fmt.Sprintf("node %v starting up", id)) - net.Start(id) - n = 5000 + for range time.Tick(10 * time.Second) { + id := ids[rand.Intn(len(ids))] + go func() { + log.Error("stopping node", "id", id) + if err := net.Stop(id); err != nil { + log.Error("error stopping node", "id", id, "err", err) + return } - }(id) + + time.Sleep(3 * time.Second) + + log.Error("starting node", "id", id) + if err := net.Start(id); err != nil { + log.Error("error starting node", "id", id, "err", err) + return + } + }() } } From 584d6e787665078279859a0011558baa6043a725 Mon Sep 17 00:00:00 2001 From: zelig Date: Wed, 17 May 2017 08:26:38 -0700 Subject: [PATCH 09/24] swarm/network: minor change to tests and no pss in bzz proto --- swarm/network/discovery_test.go | 2 +- swarm/network/hive_test.go | 2 +- swarm/network/protocol.go | 70 +++++-------------- .../simulations/discovery/discovery_test.go | 26 +++---- swarm/network/simulations/overlay.go | 13 ++-- 5 files changed, 41 insertions(+), 72 deletions(-) diff --git a/swarm/network/discovery_test.go b/swarm/network/discovery_test.go index 91a280e0df..274d046740 100644 --- a/swarm/network/discovery_test.go +++ b/swarm/network/discovery_test.go @@ -25,7 +25,7 @@ func TestDiscovery(t *testing.T) { return p.Run(dp.HandleMsg) } - s := newBzzBaseTester(t, 1, addr, DiscoveryProtocol, run) + s := newBzzBaseTester(t, 1, addr, DiscoverySpec, run) defer s.Stop() s.TestExchanges(p2ptest.Exchange{ diff --git a/swarm/network/hive_test.go b/swarm/network/hive_test.go index 8cd11b13a6..a37b5c27ba 100644 --- a/swarm/network/hive_test.go +++ b/swarm/network/hive_test.go @@ -37,7 +37,7 @@ func newHiveTester(t *testing.T, params *HiveParams) (*bzzTester, *Hive) { to := NewKademlia(addr.OAddr, NewKadParams()) pp := NewHive(params, to, nil) // hive - return newBzzBaseTester(t, 1, addr, DiscoveryProtocol, pp.Run), pp + return newBzzBaseTester(t, 1, addr, DiscoverySpec, pp.Run), pp } func TestRegisterAndConnect(t *testing.T) { diff --git a/swarm/network/protocol.go b/swarm/network/protocol.go index 9ef52f3dc4..dbaa8609af 100644 --- a/swarm/network/protocol.go +++ b/swarm/network/protocol.go @@ -20,7 +20,6 @@ import ( "context" "errors" "fmt" - "net" "sync" "time" @@ -38,7 +37,7 @@ const ( ProtocolMaxMsgSize = 10 * 1024 * 1024 ) -var BzzProtocol = &protocols.Spec{ +var BzzHandshakeSpec = &protocols.Spec{ Name: "bzz", Version: 1, MaxMsgSize: 10 * 1024 * 1024, @@ -47,7 +46,7 @@ var BzzProtocol = &protocols.Spec{ }, } -var DiscoveryProtocol = &protocols.Spec{ +var DiscoverySpec = &protocols.Spec{ Name: "hive", Version: 1, MaxMsgSize: 10 * 1024 * 1024, @@ -58,15 +57,6 @@ var DiscoveryProtocol = &protocols.Spec{ }, } -var PssProtocol = &protocols.Spec{ - Name: "pss", - Version: 1, - MaxMsgSize: 10 * 1024 * 1024, - Messages: []interface{}{ - PssMsg{}, - }, -} - // the Addr interface that peerPool needs type Addr interface { OverlayPeer @@ -98,75 +88,54 @@ type Store interface { Save(string, []byte) error } +// BzzConfig captures the config params used by the hive type BzzConfig struct { OverlayAddr []byte UnderlayAddr []byte - - KadParams *KadParams - HiveParams *HiveParams - PssParams *PssParams - - Store Store + HiveParams *HiveParams } // Bzz is the swarm protocol bundle type Bzz struct { - Kademlia *Kademlia - Hive *Hive - Pss *Pss - + Hive *Hive localAddr *bzzAddr mtx sync.Mutex handshakes map[discover.NodeID]*bzzHandshake } // NewBzz is the swarm protocol constructor -func NewBzz(config *BzzConfig) *Bzz { - kademlia := NewKademlia(config.OverlayAddr, config.KadParams) - bzz := &Bzz{ - Kademlia: kademlia, - Hive: NewHive(config.HiveParams, kademlia, config.Store), +func NewBzz(config *BzzConfig, kad Overlay, store Store) *Bzz { + return &Bzz{ + Hive: NewHive(config.HiveParams, kad, store), localAddr: &bzzAddr{config.OverlayAddr, config.UnderlayAddr}, handshakes: make(map[discover.NodeID]*bzzHandshake), } - if config.PssParams != nil { - bzz.Pss = NewPss(kademlia, config.PssParams) - } - return bzz } // Bzz implements the node.Service interface, offers Protocols // * handshake/hive // * discovery -// * pss func (b *Bzz) Protocols() []p2p.Protocol { return []p2p.Protocol{ { - Name: BzzProtocol.Name, - Version: BzzProtocol.Version, - Length: BzzProtocol.Length(), + Name: BzzHandshakeSpec.Name, + Version: BzzHandshakeSpec.Version, + Length: BzzHandshakeSpec.Length(), Run: b.runHandshake, }, { - Name: DiscoveryProtocol.Name, - Version: DiscoveryProtocol.Version, - Length: DiscoveryProtocol.Length(), - Run: b.runProtocol(DiscoveryProtocol, b.Hive.Run), + Name: DiscoverySpec.Name, + Version: DiscoverySpec.Version, + Length: DiscoverySpec.Length(), + Run: b.runProtocol(DiscoverySpec, b.Hive.Run), NodeInfo: b.Hive.NodeInfo, PeerInfo: b.Hive.PeerInfo, }, - { - Name: PssProtocol.Name, - Version: PssProtocol.Version, - Length: PssProtocol.Length(), - Run: b.runProtocol(PssProtocol, b.Pss.Run), - }, } } // Bzz implements the node.Service interface, offers APIs: // * hive -// * pss func (b *Bzz) APIs() []rpc.API { return []rpc.API{{ Namespace: "hive", @@ -232,7 +201,7 @@ func (b *Bzz) getHandshake(peerID discover.NodeID) *bzzHandshake { handshake, ok := b.handshakes[peerID] if !ok { handshake = &bzzHandshake{ - Version: uint64(BzzProtocol.Version), + Version: uint64(BzzHandshakeSpec.Version), NetworkId: uint64(NetworkId), Addr: b.localAddr, done: make(chan struct{}), @@ -298,7 +267,7 @@ func (self *bzzHandshake) Perform(p *p2p.Peer, rw p2p.MsgReadWriter) (err error) self.err = err close(self.done) }() - peer := protocols.NewPeer(p, rw, BzzProtocol) + peer := protocols.NewPeer(p, rw, BzzHandshakeSpec) ctx, cancel := context.WithTimeout(context.Background(), bzzHandshakeTimeout) defer cancel() hs, err := peer.Handshake(ctx, self) @@ -378,8 +347,5 @@ func NewNodeIdFromAddr(addr Addr) *adapters.NodeId { // the overlay address is derived as the hash of the nodeId func NewAddrFromNodeId(n *adapters.NodeId) *bzzAddr { id := n.NodeID - return &bzzAddr{ - OAddr: crypto.Keccak256(id[:]), - UAddr: []byte(discover.NewNode(id, net.IP{127, 0, 0, 1}, 30303, 30303).String()), - } + return &bzzAddr{crypto.Keccak256(id[:]), id[:]} } diff --git a/swarm/network/simulations/discovery/discovery_test.go b/swarm/network/simulations/discovery/discovery_test.go index c436f20025..3e1fde34b3 100644 --- a/swarm/network/simulations/discovery/discovery_test.go +++ b/swarm/network/simulations/discovery/discovery_test.go @@ -179,21 +179,23 @@ func triggerChecks(trigger chan *adapters.NodeId, net *simulations.Network, id * func newService(id *adapters.NodeId, snapshot []byte) node.Service { addr := network.NewAddrFromNodeId(id) + kp := network.NewKadParams() + kp.MinProxBinSize = 2 + kp.MaxBinSize = 3 + kp.MinBinSize = 1 + kp.MaxRetries = 1000 + kp.RetryExponent = 2 + kp.RetryInterval = 1000000 + kad := network.NewKademlia(addr.Over(), kp) + + hp := network.NewHiveParams() + hp.KeepAliveInterval = time.Second + config := &network.BzzConfig{ OverlayAddr: addr.Over(), UnderlayAddr: addr.Under(), - KadParams: network.NewKadParams(), - HiveParams: network.NewHiveParams(), + HiveParams: hp, } - config.KadParams.MinProxBinSize = 2 - config.KadParams.MaxBinSize = 3 - config.KadParams.MinBinSize = 1 - config.KadParams.MaxRetries = 1000 - config.KadParams.RetryExponent = 2 - config.KadParams.RetryInterval = 1000000 - - config.HiveParams.KeepAliveInterval = time.Second - - return network.NewBzz(config) + return network.NewBzz(config, kad, nil) } diff --git a/swarm/network/simulations/overlay.go b/swarm/network/simulations/overlay.go index a18efcf03a..e415539073 100644 --- a/swarm/network/simulations/overlay.go +++ b/swarm/network/simulations/overlay.go @@ -62,25 +62,26 @@ func (s *Simulation) NewService(id *adapters.NodeId, snapshot []byte) node.Servi s.mtx.Unlock() addr := network.NewAddrFromNodeId(id) - kp := network.NewKadParams() + kp := network.NewKadParams() kp.MinProxBinSize = 2 kp.MaxBinSize = 3 kp.MinBinSize = 1 kp.MaxRetries = 1000 kp.RetryExponent = 2 kp.RetryInterval = 1000000 + kad := network.NewKademlia(addr.Over(), kp) hp := network.NewHiveParams() - hp.KeepAliveInterval = 5 * time.Second + hp.KeepAliveInterval = 3 * time.Second + config := &network.BzzConfig{ OverlayAddr: addr.Over(), UnderlayAddr: addr.Under(), - KadParams: kp, HiveParams: hp, - Store: store, } - return network.NewBzz(config) + + return network.NewBzz(config, kad, store) } func createMockers() map[string]*simulations.MockerConfig { @@ -112,7 +113,7 @@ func setupMocker(net *simulations.Network) []*adapters.NodeId { conf := net.Config() conf.DefaultService = "overlay" - nodeCount := 30 + nodeCount := 50 ids := make([]*adapters.NodeId, nodeCount) for i := 0; i < nodeCount; i++ { node, err := net.NewNode() From c9d42b9cf36a673e57579d59395b7cb54a7118db Mon Sep 17 00:00:00 2001 From: Lewis Marshall Date: Wed, 17 May 2017 10:40:23 -0700 Subject: [PATCH 10/24] swarm/network: Fix NewAddrFromNodeId Signed-off-by: Lewis Marshall --- swarm/network/protocol.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/swarm/network/protocol.go b/swarm/network/protocol.go index dbaa8609af..910173800d 100644 --- a/swarm/network/protocol.go +++ b/swarm/network/protocol.go @@ -20,6 +20,7 @@ import ( "context" "errors" "fmt" + "net" "sync" "time" @@ -347,5 +348,8 @@ func NewNodeIdFromAddr(addr Addr) *adapters.NodeId { // the overlay address is derived as the hash of the nodeId func NewAddrFromNodeId(n *adapters.NodeId) *bzzAddr { id := n.NodeID - return &bzzAddr{crypto.Keccak256(id[:]), id[:]} + return &bzzAddr{ + OAddr: crypto.Keccak256(id[:]), + UAddr: []byte(discover.NewNode(id, net.IP{127, 0, 0, 1}, 30303, 30303).String()), + } } From 39992d5e1bdfb81117a30e11045304ac9477fecb Mon Sep 17 00:00:00 2001 From: Lewis Marshall Date: Wed, 17 May 2017 15:47:13 -0700 Subject: [PATCH 11/24] p2p/simulations: Support stopping, starting and deleting networks Signed-off-by: Lewis Marshall --- p2p/simulations/http.go | 58 ++++++++++++++++++++++++++++++++++++++ p2p/simulations/network.go | 24 ++++++++++++++++ 2 files changed, 82 insertions(+) diff --git a/p2p/simulations/http.go b/p2p/simulations/http.go index acc3888b1a..a0f30c7460 100644 --- a/p2p/simulations/http.go +++ b/p2p/simulations/http.go @@ -59,6 +59,21 @@ func (c *Client) GetNetwork(networkID string) (*Network, error) { return network, c.Get(fmt.Sprintf("/networks/%s", networkID), network) } +// StartNetwork starts all existing nodes in a simulation network +func (c *Client) StartNetwork(networkID string) error { + return c.Post(fmt.Sprintf("/networks/%s/start", networkID), nil, nil) +} + +// StopNetwork stops all existing nodes in a simulation network +func (c *Client) StopNetwork(networkID string) error { + return c.Post(fmt.Sprintf("/networks/%s/stop", networkID), nil, nil) +} + +// DeleteNetwork stops and deletes a simulation network +func (c *Client) DeleteNetwork(networkID string) error { + return c.Delete(fmt.Sprintf("/networks/%s", networkID)) +} + // CreateSnapshot creates a network snapshot func (c *Client) CreateSnapshot(networkID string) (*Snapshot, error) { snap := &Snapshot{} @@ -276,6 +291,9 @@ func NewServer(config *ServerConfig) *Server { s.POST("/networks", s.CreateNetwork) s.GET("/networks", s.GetNetworks) s.GET("/networks/:netid", s.GetNetwork) + s.POST("/networks/:netid/start", s.StartNetwork) + s.POST("/networks/:netid/stop", s.StopNetwork) + s.DELETE("/networks/:netid", s.DeleteNetwork) s.GET("/networks/:netid/events", s.StreamNetworkEvents) s.GET("/networks/:netid/snapshot", s.CreateSnapshot) s.POST("/networks/:netid/snapshot", s.LoadSnapshot) @@ -341,6 +359,46 @@ func (s *Server) GetNetwork(w http.ResponseWriter, req *http.Request) { s.JSON(w, http.StatusOK, network) } +// StartNetwork starts all nodes in a network +func (s *Server) StartNetwork(w http.ResponseWriter, req *http.Request) { + network := req.Context().Value("network").(*Network) + + if err := network.StartAll(); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + w.WriteHeader(http.StatusOK) +} + +// StopNetwork stops all nodes in a network +func (s *Server) StopNetwork(w http.ResponseWriter, req *http.Request) { + network := req.Context().Value("network").(*Network) + + if err := network.StopAll(); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + w.WriteHeader(http.StatusOK) +} + +// DeleteNetwork stops all nodes in a network and deletes it +func (s *Server) DeleteNetwork(w http.ResponseWriter, req *http.Request) { + network := req.Context().Value("network").(*Network) + + if err := network.StopAll(); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + s.mtx.Lock() + delete(s.networks, network.Id) + s.mtx.Unlock() + + w.WriteHeader(http.StatusOK) +} + //Get the info for a particular mocker func (s *Server) GetMocker(w http.ResponseWriter, req *http.Request) { m := make(map[string]string) diff --git a/p2p/simulations/network.go b/p2p/simulations/network.go index 4254ff4ba0..77cd4b9cbd 100644 --- a/p2p/simulations/network.go +++ b/p2p/simulations/network.go @@ -260,6 +260,30 @@ func (self *Conn) nodesUp() error { return nil } +func (self *Network) StartAll() error { + for _, node := range self.Nodes { + if node.Up { + continue + } + if err := self.Start(node.ID()); err != nil { + return err + } + } + return nil +} + +func (self *Network) StopAll() error { + for _, node := range self.Nodes { + if !node.Up { + continue + } + if err := self.Stop(node.ID()); err != nil { + return err + } + } + return nil +} + // Start(id) starts up the node (relevant only for instance with own p2p or remote) func (self *Network) Start(id *adapters.NodeId) error { return self.startWithSnapshot(id, nil) From 7411066425db22a3ace6c8e48cb5690e0739cd1f Mon Sep 17 00:00:00 2001 From: Lewis Marshall Date: Wed, 17 May 2017 20:35:07 -0700 Subject: [PATCH 12/24] swarm/network: Fix bzzAddr.String() Signed-off-by: Lewis Marshall --- swarm/network/protocol.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/swarm/network/protocol.go b/swarm/network/protocol.go index 910173800d..1678e2de5a 100644 --- a/swarm/network/protocol.go +++ b/swarm/network/protocol.go @@ -321,7 +321,7 @@ func (self *bzzAddr) Update(a OverlayAddr) OverlayAddr { } func (self *bzzAddr) String() string { - return fmt.Sprintf("%x <%x>", self.OAddr, self.UAddr) + return fmt.Sprintf("%x <%s>", self.OAddr, self.UAddr) } // RandomAddr is a utility method generating an address from a public key From be0335c889f80719e751dd6e92a716589689d658 Mon Sep 17 00:00:00 2001 From: Lewis Marshall Date: Wed, 17 May 2017 21:53:50 -0700 Subject: [PATCH 13/24] p2p/simulations: Disable message events Signed-off-by: Lewis Marshall --- p2p/simulations/adapters/exec.go | 2 +- p2p/simulations/adapters/inproc.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/p2p/simulations/adapters/exec.go b/p2p/simulations/adapters/exec.go index 706cdcfd9f..88d99b22b5 100644 --- a/p2p/simulations/adapters/exec.go +++ b/p2p/simulations/adapters/exec.go @@ -63,7 +63,7 @@ func (e *ExecAdapter) NewNode(config *NodeConfig) (Node, error) { Node: config, } conf.Stack.DataDir = filepath.Join(dir, "data") - conf.Stack.P2P.EnableMsgEvents = true + conf.Stack.P2P.EnableMsgEvents = false conf.Stack.P2P.NoDiscovery = true conf.Stack.P2P.NAT = nil diff --git a/p2p/simulations/adapters/inproc.go b/p2p/simulations/adapters/inproc.go index 3226446927..a714564019 100644 --- a/p2p/simulations/adapters/inproc.go +++ b/p2p/simulations/adapters/inproc.go @@ -186,7 +186,7 @@ func (self *SimNode) Start(snapshot []byte) error { MaxPeers: math.MaxInt32, NoDiscovery: true, Dialer: self.adapter, - EnableMsgEvents: true, + EnableMsgEvents: false, }, NoUSB: true, }) From 0b28cf8544ae897495103fee8aa3bd25e259feac Mon Sep 17 00:00:00 2001 From: Lewis Marshall Date: Wed, 17 May 2017 21:56:06 -0700 Subject: [PATCH 14/24] swarm/network: Remove hive keep alive toggle Signed-off-by: Lewis Marshall --- swarm/network/hive.go | 36 ++++++++++-------------------------- swarm/network/hive_test.go | 36 +++--------------------------------- 2 files changed, 13 insertions(+), 59 deletions(-) diff --git a/swarm/network/hive.go b/swarm/network/hive.go index 1a361cb86f..52a3521d38 100644 --- a/swarm/network/hive.go +++ b/swarm/network/hive.go @@ -82,12 +82,11 @@ type Hive struct { store Store // bookkeeping - lock sync.Mutex - quit chan bool - toggle chan bool - more chan bool + lock sync.Mutex + quit chan bool + more chan bool - newTicker func() hiveTicker + tick <-chan time.Time } // Hive constructor embeds both arguments @@ -112,7 +111,6 @@ func (self *Hive) Start(server *p2p.Server) error { return err } } - self.toggle = make(chan bool) self.more = make(chan bool, 1) self.quit = make(chan bool) log.Debug("hive started") @@ -150,10 +148,9 @@ func (self *Hive) Start(server *p2p.Server) error { log.Info(fmt.Sprintf("%v", self)) select { - case self.toggle <- want: - log.Trace(fmt.Sprintf("keep hive alive: %v", want)) case <-self.quit: return + default: } } }() @@ -250,29 +247,16 @@ func (t *timeTicker) Ch() <-chan time.Time { // it goes to sleep mode if table is saturated // it restarts if the table becomes non-full again due to disconnections func (self *Hive) keepAlive() { - if self.newTicker == nil { - self.newTicker = func() hiveTicker { - return &timeTicker{time.NewTicker(self.KeepAliveInterval)} - } + if self.tick == nil { + ticker := time.NewTicker(self.KeepAliveInterval) + defer ticker.Stop() + self.tick = ticker.C } - ticker := self.newTicker() - tick := ticker.Ch() for { select { - case <-tick: + case <-self.tick: log.Debug("wake up: make hive alive") self.wake() - case need := <-self.toggle: - if ticker == nil && need { - ticker = self.newTicker() - tick = ticker.Ch() - } - // if hive saturated, no more peers asked - if ticker != nil && !need { - ticker.Stop() - ticker = nil - tick = nil - } case <-self.quit: return } diff --git a/swarm/network/hive_test.go b/swarm/network/hive_test.go index a37b5c27ba..5a1b3f6ca8 100644 --- a/swarm/network/hive_test.go +++ b/swarm/network/hive_test.go @@ -1,36 +1,12 @@ package network import ( - "sync" "testing" "time" - "github.com/ethereum/go-ethereum/p2p/simulations/adapters" p2ptest "github.com/ethereum/go-ethereum/p2p/testing" ) -type testConnect struct { - mu sync.Mutex - conns []string - connectf func(c string) error - ticker chan time.Time -} - -func (self *testConnect) Ch() <-chan time.Time { - return self.ticker -} - -func (self *testConnect) Stop() { -} - -func (self *testConnect) connect(na string) error { - self.mu.Lock() - defer self.mu.Unlock() - self.conns = append(self.conns, na) - self.connectf(na) - return nil -} - func newHiveTester(t *testing.T, params *HiveParams) (*bzzTester, *Hive) { // setup addr := RandomAddr() // tested peers peer address @@ -56,17 +32,11 @@ func TestRegisterAndConnect(t *testing.T) { pp.Register(ch) // start the hive and wait for the connection - tc := &testConnect{ - connectf: func(c string) error { - s.Connect(adapters.NewNodeIdFromHex(c)) - return nil - }, - ticker: make(chan time.Time), - } - pp.newTicker = func() hiveTicker { return tc } + tick := make(chan time.Time) + pp.tick = tick pp.Start(s.Server) defer pp.Stop() - tc.ticker <- time.Now() + tick <- time.Now() // retrieve and broadcast ord := raddr.Over()[0] / 32 o := 0 From 0534fa4f97d1ae42f31f107633b4b50053860814 Mon Sep 17 00:00:00 2001 From: Lewis Marshall Date: Wed, 17 May 2017 21:56:30 -0700 Subject: [PATCH 15/24] swarm/network: Wake the hive when a peer is registered Signed-off-by: Lewis Marshall --- swarm/network/hive.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/swarm/network/hive.go b/swarm/network/hive.go index 52a3521d38..2feb7d98d6 100644 --- a/swarm/network/hive.go +++ b/swarm/network/hive.go @@ -167,7 +167,7 @@ func (self *Hive) Stop() { } func (self *Hive) Run(p *bzzPeer) error { - dp := NewDiscovery(p, self.Overlay) + dp := NewDiscovery(p, self) log.Debug(fmt.Sprintf("to add new bee %v", p)) self.On(dp) self.wake() @@ -199,6 +199,11 @@ func (self *Hive) Healthy() bool { return true } +func (self *Hive) Register(peers chan OverlayAddr) error { + defer self.wake() + return self.Overlay.Register(peers) +} + // wake triggers func (self *Hive) wake() { select { From 30aee9e3d6c3c20cc576bc84b0901e1cc6e49ae0 Mon Sep 17 00:00:00 2001 From: Lewis Marshall Date: Wed, 17 May 2017 21:57:18 -0700 Subject: [PATCH 16/24] swarm/network/simulations: Update overlay sim Signed-off-by: Lewis Marshall --- swarm/network/simulations/overlay.go | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/swarm/network/simulations/overlay.go b/swarm/network/simulations/overlay.go index e415539073..70bd53e3ec 100644 --- a/swarm/network/simulations/overlay.go +++ b/swarm/network/simulations/overlay.go @@ -65,11 +65,11 @@ func (s *Simulation) NewService(id *adapters.NodeId, snapshot []byte) node.Servi kp := network.NewKadParams() kp.MinProxBinSize = 2 - kp.MaxBinSize = 3 - kp.MinBinSize = 1 + kp.MaxBinSize = 8 + kp.MinBinSize = 2 kp.MaxRetries = 1000 kp.RetryExponent = 2 - kp.RetryInterval = 1000000 + kp.RetryInterval = 1000 kad := network.NewKademlia(addr.Over(), kp) hp := network.NewHiveParams() @@ -124,8 +124,6 @@ func setupMocker(net *simulations.Network) []*adapters.NodeId { } for _, id := range ids { - n := rand.Intn(1000) - time.Sleep(time.Duration(n) * time.Millisecond) if err := net.Start(id); err != nil { panic(err.Error()) } @@ -227,7 +225,7 @@ func main() { config := &simulations.ServerConfig{ NewAdapter: func() adapters.NodeAdapter { return adapters.NewSimAdapter(services) }, - DefaultMockerId: "start-stop", + DefaultMockerId: "bootNet", Mockers: mockers, } From 6de0d73449d57318f8d976aae784f64d3b5ecc43 Mon Sep 17 00:00:00 2001 From: zelig Date: Wed, 17 May 2017 14:09:38 -0700 Subject: [PATCH 17/24] swarm/network: kademlia health check for discovery test --- swarm/network/hive.go | 9 +-- swarm/network/kademlia.go | 65 +++++++++++++++++++ swarm/network/protocol.go | 8 ++- .../simulations/discovery/discovery_test.go | 6 +- 4 files changed, 77 insertions(+), 11 deletions(-) diff --git a/swarm/network/hive.go b/swarm/network/hive.go index 2feb7d98d6..9fb20d2815 100644 --- a/swarm/network/hive.go +++ b/swarm/network/hive.go @@ -55,6 +55,7 @@ type Overlay interface { String() string BaseAddr() []byte + Healthy([][]byte) bool } // HiveParams holds the config options to hive @@ -191,14 +192,6 @@ func (self *Hive) PeerInfo(id discover.NodeID) interface{} { return interface{}(addr) } -// Healthy reports the health state of the kademlia connectivity -// -func (self *Hive) Healthy() bool { - // TODO: determine if we have enough peers to consider the network - // to be healthy - return true -} - func (self *Hive) Register(peers chan OverlayAddr) error { defer self.wake() return self.Overlay.Register(peers) diff --git a/swarm/network/kademlia.go b/swarm/network/kademlia.go index 80ecad5962..dc8faa6d0d 100644 --- a/swarm/network/kademlia.go +++ b/swarm/network/kademlia.go @@ -23,6 +23,8 @@ import ( "time" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/p2p/discover" + "github.com/ethereum/go-ethereum/p2p/simulations/adapters" "github.com/ethereum/go-ethereum/pot" ) @@ -499,3 +501,66 @@ func (self *Kademlia) Prune(c <-chan time.Time) { } }() } + +func NewPeerPot(kadMinProxSize int, ids ...*adapters.NodeId) map[discover.NodeID][][]byte { + // create a table of all nodes for health check + np := pot.NewPot(nil, 0) + for _, id := range ids { + o := ToOverlayAddr(id) + np, _, _ = pot.Add(np, pot.NewBytesVal(o, nil)) + } + nnmap := make(map[discover.NodeID][][]byte) + + for _, id := range ids { + pl := 0 + var nns [][]byte + np.EachNeighbour(pot.NewBytesVal(id.Bytes(), nil), func(val pot.PotVal, po int) bool { + a := val.(pot.BytesAddress).Address() + nns = append(nns, a) + if len(nns) >= kadMinProxSize { + pl = po + } + return pl == 0 || pl == po + }) + nnmap[id.NodeID] = nns + } + return nnmap +} + +func (self *Kademlia) FirstEmptyBin() (i int) { + i = -1 + self.conns.EachBin(pot.NewBytesVal(self.base, nil), 0, func(po, size int, f func(func(val pot.PotVal, i int) bool) bool) bool { + if po > i+1 { + i = po + return false + } + i = po + return true + }) + return i +} + +func (self *Kademlia) Full() bool { + return self.FirstEmptyBin() >= self.Depth() +} + +// Healthy reports the health state of the kademlia connectivity +// +func (self *Kademlia) Healthy(peers [][]byte) bool { + return self.gotNearestNeighbours(peers) && self.Full() +} + +func (self *Kademlia) gotNearestNeighbours(peers [][]byte) (got bool) { + pm := make(map[string]bool) + for _, p := range peers { + pm[string(p)] = true + } + self.EachConn(nil, 255, func(p OverlayConn, po int, nn bool) bool { + if !nn { + return false + } + _, got = pm[string(p.Address())] + return got + }) + return got +} diff --git a/swarm/network/protocol.go b/swarm/network/protocol.go index 1678e2de5a..cb8b4eca2f 100644 --- a/swarm/network/protocol.go +++ b/swarm/network/protocol.go @@ -64,6 +64,7 @@ type Addr interface { Over() []byte Under() []byte String() string + Update(OverlayAddr) OverlayAddr } // Peer interface represents an live peer connection @@ -349,7 +350,12 @@ func NewNodeIdFromAddr(addr Addr) *adapters.NodeId { func NewAddrFromNodeId(n *adapters.NodeId) *bzzAddr { id := n.NodeID return &bzzAddr{ - OAddr: crypto.Keccak256(id[:]), + OAddr: ToOverlayAddr(n), UAddr: []byte(discover.NewNode(id, net.IP{127, 0, 0, 1}, 30303, 30303).String()), } } + +// ToOverlayAddr creates an overlayaddress from NodeID +func ToOverlayAddr(id *adapters.NodeId) []byte { + return crypto.Keccak256(id.Bytes()) +} diff --git a/swarm/network/simulations/discovery/discovery_test.go b/swarm/network/simulations/discovery/discovery_test.go index 3e1fde34b3..a02e1d67f3 100644 --- a/swarm/network/simulations/discovery/discovery_test.go +++ b/swarm/network/simulations/discovery/discovery_test.go @@ -19,6 +19,7 @@ import ( // serviceName is used with the exec adapter so the exec'd binary knows which // service to execute const serviceName = "discovery" +const testMinProxBinSize = 2 var services = adapters.Services{ serviceName: newService, @@ -94,6 +95,7 @@ func testDiscoverySimulation(t *testing.T, adapter adapters.NodeAdapter) { } return nil } + nnmap := network.NewPeerPot(testMinProxBinSize, ids...) check := func(ctx context.Context, id *adapters.NodeId) (bool, error) { select { case <-ctx.Done(): @@ -110,7 +112,7 @@ func testDiscoverySimulation(t *testing.T, adapter adapters.NodeAdapter) { return false, fmt.Errorf("error getting node client: %s", err) } var healthy bool - if err := client.Call(&healthy, "hive_healthy", nil); err != nil { + if err := client.Call(&healthy, "hive_healthy", nnmap[id.NodeID]); err != nil { return false, fmt.Errorf("error getting node health: %s", err) } return healthy, nil @@ -180,7 +182,7 @@ func newService(id *adapters.NodeId, snapshot []byte) node.Service { addr := network.NewAddrFromNodeId(id) kp := network.NewKadParams() - kp.MinProxBinSize = 2 + kp.MinProxBinSize = testMinProxBinSize kp.MaxBinSize = 3 kp.MinBinSize = 1 kp.MaxRetries = 1000 From 96331ac340d55c452c0f446755c3be7ad2e7348e Mon Sep 17 00:00:00 2001 From: nolash Date: Mon, 15 May 2017 02:17:10 +0200 Subject: [PATCH 18/24] swarm, swarm/pss, swarm/network: pssclient rw reads and writes from websocket pss is now separate package pss event feed removed, multipile handlers allowed instead NOTE! pss tests are removed and will be rewritten remove pssclient outbox, psscache bug fix WIP tests reinstation --- p2p/simulations/adapters/docker.go | 15 +- p2p/simulations/adapters/exec.go | 15 +- p2p/simulations/adapters/inproc.go | 65 ++- p2p/simulations/adapters/types.go | 17 +- p2p/simulations/network.go | 6 +- p2p/testing/protocolsession.go | 4 +- p2p/testing/protocoltester.go | 4 +- swarm/network/pssapi.go | 66 --- swarm/pss/client.go | 228 +++++++++++ swarm/pss/client_test.go | 173 ++++++++ swarm/pss/common.go | 66 +++ swarm/{network => pss}/pss.go | 622 +++++++++++++++-------------- swarm/{network => pss}/pss_test.go | 587 ++++++++++++++++++++------- swarm/pss/pssapi.go | 76 ++++ swarm/swarm.go | 1 + 15 files changed, 1414 insertions(+), 531 deletions(-) delete mode 100644 swarm/network/pssapi.go create mode 100644 swarm/pss/client.go create mode 100644 swarm/pss/client_test.go create mode 100644 swarm/pss/common.go rename swarm/{network => pss}/pss.go (50%) rename swarm/{network => pss}/pss_test.go (73%) create mode 100644 swarm/pss/pssapi.go diff --git a/p2p/simulations/adapters/docker.go b/p2p/simulations/adapters/docker.go index a9460df0df..7b58e7cfeb 100644 --- a/p2p/simulations/adapters/docker.go +++ b/p2p/simulations/adapters/docker.go @@ -42,8 +42,10 @@ func (d *DockerAdapter) Name() string { // NewNode returns a new DockerNode using the given config func (d *DockerAdapter) NewNode(config *NodeConfig) (Node, error) { - if _, exists := serviceFuncs[config.Service]; !exists { - return nil, fmt.Errorf("unknown node service %q", config.Service) + for _, name := range config.Services { + if _, exists := serviceFuncs[name]; !exists { + return nil, fmt.Errorf("unknown node service %q", name) + } } // generate the config @@ -60,6 +62,7 @@ func (d *DockerAdapter) NewNode(config *NodeConfig) (Node, error) { ExecNode: ExecNode{ ID: config.Id, Config: conf, + Services: config.Services, }, } node.newCmd = node.dockerCommand @@ -81,12 +84,16 @@ func (n *DockerNode) dockerCommand() *exec.Cmd { return exec.Command( "sh", "-c", fmt.Sprintf( - `exec docker run --interactive --env _P2P_NODE_CONFIG="${_P2P_NODE_CONFIG}" %s p2p-node %s %s`, - dockerImage, n.Config.Node.Service, n.ID.String(), + `exec docker run --interactive --env _P2P_NODE_CONFIG="${_P2P_NODE_CONFIG}" --env _P2P_NODE_KEY="${_P2P_NODE_KEY}" %s p2p-node %s %s`, + dockerImage, n.Services[0], n.ID.String(), ), ) } +func (n *DockerNode) GetService(name string) node.Service { + return nil +} + // dockerImage is the name of the docker image const dockerImage = "p2p-node" diff --git a/p2p/simulations/adapters/exec.go b/p2p/simulations/adapters/exec.go index 88d99b22b5..376c03c6db 100644 --- a/p2p/simulations/adapters/exec.go +++ b/p2p/simulations/adapters/exec.go @@ -46,8 +46,10 @@ func (e *ExecAdapter) Name() string { // NewNode returns a new ExecNode using the given config func (e *ExecAdapter) NewNode(config *NodeConfig) (Node, error) { - if _, exists := serviceFuncs[config.Service]; !exists { - return nil, fmt.Errorf("unknown node service %q", config.Service) + for _, name := range config.Services { + if _, exists := serviceFuncs[name]; !exists { + return nil, fmt.Errorf("unknown node service %q", name) + } } // create the node directory using the first 12 characters of the ID @@ -75,6 +77,7 @@ func (e *ExecAdapter) NewNode(config *NodeConfig) (Node, error) { ID: config.Id, Dir: dir, Config: conf, + Services: config.Services, } node.newCmd = node.execCommand return node, nil @@ -93,6 +96,7 @@ type ExecNode struct { Config *execNodeConfig Cmd *exec.Cmd Info *p2p.NodeInfo + Services []string client *rpc.Client rpcMux *rpcMux @@ -164,13 +168,18 @@ func (n *ExecNode) Start(snapshot []byte) (err error) { return nil } + +func (n *ExecNode) GetService(name string) node.Service { + return nil +} + // execCommand returns a command which runs the node locally by exec'ing // the current binary but setting argv[0] to "p2p-node" so that the child // runs execP2PNode func (n *ExecNode) execCommand() *exec.Cmd { return &exec.Cmd{ Path: reexec.Self(), - Args: []string{"p2p-node", n.Config.Node.Service, n.ID.String()}, + Args: []string{"p2p-node", n.Services[0], n.ID.String()}, } } diff --git a/p2p/simulations/adapters/inproc.go b/p2p/simulations/adapters/inproc.go index a714564019..5c1c38aec4 100644 --- a/p2p/simulations/adapters/inproc.go +++ b/p2p/simulations/adapters/inproc.go @@ -24,6 +24,7 @@ import ( "sync" "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/discover" @@ -55,6 +56,8 @@ func (s *SimAdapter) Name() string { // NewNode returns a new SimNode using the given config func (s *SimAdapter) NewNode(config *NodeConfig) (Node, error) { + var nodeprotos []p2p.Protocol + s.mtx.Lock() defer s.mtx.Unlock() @@ -65,6 +68,7 @@ func (s *SimAdapter) NewNode(config *NodeConfig) (Node, error) { } // check the service is valid and initialize it +/* serviceFunc, exists := s.services[config.Service] if !exists { return nil, fmt.Errorf("unknown node service %q", config.Service) @@ -75,9 +79,52 @@ func (s *SimAdapter) NewNode(config *NodeConfig) (Node, error) { config: config, adapter: s, serviceFunc: serviceFunc, +*/ + //serviceFunc, exists := s.services[config.Service] + + //if !exists { + // return nil, fmt.Errorf("unknown node service %q", config.Service) + //} + //service := serviceFunc(id) + + n, err := node.New(&node.Config{ + P2P: p2p.Config{ + PrivateKey: config.PrivateKey, + MaxPeers: math.MaxInt32, + NoDiscovery: true, + Protocols: nodeprotos, + Dialer: s, + EnableMsgEvents: true, + }, + }) + if err != nil { + return nil, err } - s.nodes[id.NodeID] = node - return node, nil + + services := make(map[string]node.Service) + + for name, servicefunc := range s.services { + service := servicefunc(id) + if err := n.Register(func(ctx *node.ServiceContext) (node.Service, error) { + return service, err + }); err != nil { + return nil, err + } + for _, proto := range service.Protocols() { + nodeprotos = append(nodeprotos, proto) + } + services[name] = service + } + + simnode := &SimNode{ + Node: n, + Id: id, + services: services, + adapter: s, + config: config, + } + s.nodes[id.NodeID] = simnode + return simnode, nil } func (s *SimAdapter) Dial(dest *discover.Node) (conn net.Conn, err error) { @@ -113,7 +160,7 @@ type SimNode struct { Id *NodeId config *NodeConfig adapter *SimAdapter - serviceFunc ServiceFunc + services map[string]node.Service node *node.Node running node.Service client *rpc.Client @@ -233,13 +280,6 @@ func (self *SimNode) Stop() error { return nil } -// Service returns the underlying node.Service -func (self *SimNode) Service() node.Service { - self.lock.Lock() - defer self.lock.Unlock() - return self.running -} - func (self *SimNode) Server() *p2p.Server { self.lock.Lock() defer self.lock.Unlock() @@ -247,6 +287,11 @@ func (self *SimNode) Server() *p2p.Server { return nil } return self.node.Server() + +// Service returns a underlying node.Service of the speficied type +func (self *SimNode) GetService(servicename string) node.Service { + log.Warn("retrieving service", "name", servicename) + return self.services[servicename] } func (self *SimNode) SubscribeEvents(ch chan *p2p.PeerEvent) event.Subscription { diff --git a/p2p/simulations/adapters/types.go b/p2p/simulations/adapters/types.go index e7f15ba8f7..dacf3a2111 100644 --- a/p2p/simulations/adapters/types.go +++ b/p2p/simulations/adapters/types.go @@ -62,6 +62,9 @@ type Node interface { // Snapshot creates a snapshot of the running service Snapshot() ([]byte, error) + + // Gets a service by name + GetService(string) node.Service } // NodeAdapter is an object which creates Nodes to be used in a simulation @@ -127,11 +130,11 @@ type NodeConfig struct { // Name is a human friendly name for the node like "node01" Name string - // Service is the name of the service which should be run when starting - // the node (for SimNodes it should be the name of a service contained - // in SimAdapter.services, for other nodes it should be a service + // Services is the name of the services which should be run when starting + // the node (for SimNodes it should be the names of services contained + // in SimAdapter.services, for other nodes it should be services // registered by calling the RegisterService function) - Service string + Services []string } // nodeConfigJSON is used to encode and decode NodeConfig as JSON by converting @@ -140,13 +143,13 @@ type nodeConfigJSON struct { Id string `json:"id"` PrivateKey string `json:"private_key"` Name string `json:"name"` - Service string `json:"service"` + Services []string `json:"services"` } func (n *NodeConfig) MarshalJSON() ([]byte, error) { confJSON := nodeConfigJSON{ Name: n.Name, - Service: n.Service, + Services: n.Services, } if n.Id != nil { confJSON.Id = n.Id.String() @@ -180,7 +183,7 @@ func (n *NodeConfig) UnmarshalJSON(data []byte) error { } n.Name = confJSON.Name - n.Service = confJSON.Service + n.Services = confJSON.Services return nil } diff --git a/p2p/simulations/network.go b/p2p/simulations/network.go index 77cd4b9cbd..84d06e089e 100644 --- a/p2p/simulations/network.go +++ b/p2p/simulations/network.go @@ -190,7 +190,7 @@ func (self *Msg) String() string { // NewNode adds a new node to the network with a random ID func (self *Network) NewNode() (*Node, error) { conf := adapters.RandomNodeConfig() - conf.Service = self.DefaultService + conf.Services = append(conf.Services, self.DefaultService) return self.NewNodeWithConfig(conf) } @@ -203,8 +203,8 @@ func (self *Network) NewNodeWithConfig(conf *adapters.NodeConfig) (*Node, error) if conf.Name == "" { conf.Name = fmt.Sprintf("node%02d", len(self.Nodes)+1) } - if conf.Service == "" { - conf.Service = self.DefaultService + if len(conf.Services) == 0 { + conf.Services = append(conf.Services, self.DefaultService) } _, found := self.nodeMap[id.NodeID] diff --git a/p2p/testing/protocolsession.go b/p2p/testing/protocolsession.go index 2de849885d..98c0bf7f2a 100644 --- a/p2p/testing/protocolsession.go +++ b/p2p/testing/protocolsession.go @@ -57,7 +57,7 @@ func (self *ProtocolSession) trigger(trig Trigger) error { if !ok { return fmt.Errorf("trigger: peer %v does not exist (1- %v)", trig.Peer, len(self.Ids)) } - mockNode, ok := simNode.Service().(*mockNode) + mockNode, ok := simNode.GetService("mock").(*mockNode) if !ok { return fmt.Errorf("trigger: peer %v is not a mock", trig.Peer) } @@ -92,7 +92,7 @@ func (self *ProtocolSession) expect(exp Expect) error { if !ok { return fmt.Errorf("trigger: peer %v does not exist (1- %v)", exp.Peer, len(self.Ids)) } - mockNode, ok := simNode.Service().(*mockNode) + mockNode, ok := simNode.GetService("mock").(*mockNode) if !ok { return fmt.Errorf("trigger: peer %v is not a mock", exp.Peer) } diff --git a/p2p/testing/protocoltester.go b/p2p/testing/protocoltester.go index 3b104bd12f..e34fcf0265 100644 --- a/p2p/testing/protocoltester.go +++ b/p2p/testing/protocoltester.go @@ -29,7 +29,7 @@ func NewProtocolTester(t *testing.T, id *adapters.NodeId, n int, run func(*p2p.P } adapter := adapters.NewSimAdapter(services) net := simulations.NewNetwork(adapter, &simulations.NetworkConfig{}) - if _, err := net.NewNodeWithConfig(&adapters.NodeConfig{Id: id, Service: "test"}); err != nil { + if _, err := net.NewNodeWithConfig(&adapters.NodeConfig{Id: id, Services: []string{"test"}}); err != nil { panic(err.Error()) } if err := net.Start(id); err != nil { @@ -41,7 +41,7 @@ func NewProtocolTester(t *testing.T, id *adapters.NodeId, n int, run func(*p2p.P peerIDs := make([]*adapters.NodeId, n) for i := 0; i < n; i++ { peers[i] = adapters.RandomNodeConfig() - peers[i].Service = "mock" + peers[i].Services = append(peers[i].Services, "mock") peerIDs[i] = peers[i].Id } events := make(chan *p2p.PeerEvent, 1000) diff --git a/swarm/network/pssapi.go b/swarm/network/pssapi.go deleted file mode 100644 index 82b45e8b03..0000000000 --- a/swarm/network/pssapi.go +++ /dev/null @@ -1,66 +0,0 @@ -package network - -import ( - "context" - "fmt" - - "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/rpc" -) - -type PssApi struct { - *Pss -} - -func NewPssApi(ps *Pss) *PssApi { - return &PssApi{Pss: ps} -} - -func (self *PssApi) NewMsg(ctx context.Context, topic PssTopic) (*rpc.Subscription, error) { - notifier, supported := rpc.NotifierFromContext(ctx) - if !supported { - return nil, fmt.Errorf("Subscribe not supported") - } - - sub := notifier.CreateSubscription() - - ch := make(chan []byte) - psssub, err := self.Pss.Subscribe(&topic, ch) - if err != nil { - return nil, fmt.Errorf("pss subscription topic %v (rpc sub id %v) failed: %v", topic, sub.ID, err) - } - - go func(topic PssTopic) error { - for { - select { - case msg := <-ch: - if err := notifier.Notify(sub.ID, msg); err != nil { - log.Warn(fmt.Sprintf("notification on pss sub topic %v rpc (sub %v) msg %v failed!", topic, sub.ID, msg)) - return err - } - case err := <-psssub.Err(): - log.Warn(fmt.Sprintf("caught subscription error in pss sub topic: %v", topic, err)) - return err - case <-notifier.Closed(): - log.Warn(fmt.Sprintf("rpc sub notifier closed")) - psssub.Unsubscribe() - return nil - case err := <-sub.Err(): - log.Warn(fmt.Sprintf("rpc sub closed: %v", err)) - psssub.Unsubscribe() - return nil - } - } - return nil - }(topic) - - return sub, nil -} - -func (self *PssApi) SendRaw(to []byte, topic PssTopic, msg []byte) error { - err := self.Pss.Send(to, topic, msg) - if err != nil { - return fmt.Errorf("send error: %v", err) - } - return fmt.Errorf("ok sent") -} diff --git a/swarm/pss/client.go b/swarm/pss/client.go new file mode 100644 index 0000000000..d71d7928a9 --- /dev/null +++ b/swarm/pss/client.go @@ -0,0 +1,228 @@ +package pss + +import ( + "context" + "fmt" + "sync" + "sync/atomic" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/node" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/p2p/discover" + "github.com/ethereum/go-ethereum/p2p/protocols" + "github.com/ethereum/go-ethereum/pot" + "github.com/ethereum/go-ethereum/rpc" +) + +const ( + inboxCapacity = 3000 + outboxCapacity = 100 + addrLen = common.HashLength +) + +// implements p2p.Server +// implements net.Conn +type PssClient struct { + localuri string + remoteuri string + ctx context.Context + cancel func() + subscription *rpc.ClientSubscription + topicsC chan []byte + msgC chan PssAPIMsg + quitC chan struct{} + quitting uint32 + ws *rpc.Client + lock sync.Mutex + peerPool map[PssTopic]map[pot.Address]*pssRPCRW + protos []*p2p.Protocol +} + +type pssRPCRW struct { + *PssClient + topic *PssTopic + spec *protocols.Spec + msgC chan []byte + addr pot.Address +} + +func (self *PssClient) newpssRPCRW(addr pot.Address, spec *protocols.Spec, topic *PssTopic) *pssRPCRW { + return &pssRPCRW { + PssClient: self, + topic: topic, + spec: spec, + msgC: make(chan []byte), + addr: addr, + } +} + +func (rw *pssRPCRW) ReadMsg() (p2p.Msg, error) { + msg := <- rw.msgC + log.Warn("pssrpcrw read", "msg", msg) + pmsg, err := ToP2pMsg(msg) + if err != nil { + return p2p.Msg{}, err + } + + return pmsg, nil +} + +func (rw *pssRPCRW) WriteMsg(msg p2p.Msg) error { + + ifc, found := rw.spec.NewMsg(msg.Code) + if !found { + return fmt.Errorf("could not find interface for msg #%d", msg.Code) + } + msg.Decode(ifc) + pmsg, err := newProtocolMsg(msg.Code, ifc) + if err != nil { + return fmt.Errorf("Could not render protocolmessage", "error", err) + } + + return rw.PssClient.ws.CallContext(rw.PssClient.ctx, nil, "pss_sendRaw", rw.topic, PssAPIMsg{ + Addr: rw.addr.Bytes(), + Msg: pmsg, + }) + +} + +// remotehost: hostname of node running websockets proxy to pss (default localhost) +// remoteport: port of node running websockets proxy to pss (0 = go-ethereum node default) +// secure: whether or not to use secure connection +// originhost: local if host to connect from + +func NewPssClient(ctx context.Context, cancel func(), remotehost string, remoteport int, secure bool, originhost string) *PssClient { + prefix := "ws" + + if ctx == nil { + ctx = context.Background() + cancel = func() {return} + } + pssc := &PssClient{ + msgC: make(chan PssAPIMsg), + quitC: make(chan struct{}), + peerPool: make(map[PssTopic]map[pot.Address]*pssRPCRW), + ctx: ctx, + cancel: cancel, + } + + if remotehost == "" { + remotehost = "localhost" + } + + if remoteport == 0 { + remoteport = node.DefaultWSPort + } + + if originhost == "" { + originhost = "localhost" + } + + if secure { + prefix = "wss" + } + + pssc.remoteuri = fmt.Sprintf("%s://%s:%d", prefix, remotehost, remoteport) + pssc.localuri = fmt.Sprintf("%s://%s", prefix, originhost) + + return pssc +} + +func (self *PssClient) shutdown() { + atomic.StoreUint32(&self.quitting, 1) + self.cancel() +} + +func (self *PssClient) Start() error { + log.Debug("Dialing ws", "src", self.localuri, "dst", self.remoteuri) + ws, err := rpc.DialWebsocket(self.ctx, self.remoteuri, self.localuri) + if err != nil { + return fmt.Errorf("Couldnt dial pss websocket: %v", err) + } + + self.ws = ws + + return nil +} + +func (self *PssClient) RunProtocol(proto *p2p.Protocol, spec *protocols.Spec) error { + topic := NewTopic(spec.Name, int(spec.Version)) + msgC := make(chan PssAPIMsg) + self.peerPool[topic] = make(map[pot.Address]*pssRPCRW) + sub, err := self.ws.Subscribe(self.ctx, "pss", msgC, "newMsg", topic) + if err != nil { + return fmt.Errorf("pss event subscription failed: %v", err) + } + + self.subscription = sub + + // dispatch incoming messages + go func() { + for { + select { + case msg := <- msgC: + var addr pot.Address + copy(addr[:], msg.Addr) + if self.peerPool[topic][addr] == nil { + self.peerPool[topic][addr] = self.newpssRPCRW(addr, spec, &topic) + nid, _ := discover.HexID("0x00") + p := p2p.NewPeer(nid, fmt.Sprintf("%v", addr), []p2p.Cap{}) + go proto.Run(p, self.peerPool[topic][addr]) + } + go func() { + self.peerPool[topic][addr].msgC <- msg.Msg + }() + case <-self.quitC: + self.shutdown() + return + } + } + }() + + self.protos = append(self.protos, proto) + return nil +} + +func (self *PssClient) Stop() error { + self.cancel() + return nil +} + +func (self *PssClient) AddPssPeer(addr pot.Address, spec *protocols.Spec) { + topic := NewTopic(spec.Name, int(spec.Version)) + if self.peerPool[topic][addr] == nil { + self.peerPool[topic][addr] = self.newpssRPCRW(addr, spec, &topic) + } +} + +func (self *PssClient) RemovePssPeer(addr pot.Address, spec *protocols.Spec) { + topic := NewTopic(spec.Name, int(spec.Version)) + delete(self.peerPool[topic], addr) +} + +func (self *PssClient) SubscribeEvents(ch chan *p2p.PeerEvent) event.Subscription { + log.Error("PSS client handles events internally, use the read functions instead") + return nil +} + +func (self *PssClient) PeerCount() int { + return len(self.peerPool) +} + +func (self *PssClient) NodeInfo() *p2p.NodeInfo { + return nil +} + +func (self *PssClient) PeersInfo() []*p2p.PeerInfo { + return nil +} +func (self *PssClient) AddPeer(node *discover.Node) { + log.Error("Cannot add peer in PSS with discover.Node, need swarm overlay address") +} + +func (self *PssClient) RemovePeer(node *discover.Node) { + log.Error("Cannot remove peer in PSS with discover.Node, need swarm overlay address") +} diff --git a/swarm/pss/client_test.go b/swarm/pss/client_test.go new file mode 100644 index 0000000000..05fcd2f542 --- /dev/null +++ b/swarm/pss/client_test.go @@ -0,0 +1,173 @@ +package pss + +import ( + "context" + "fmt" + "os" + "net" + "net/http" + "testing" + "time" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/node" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/p2p/discover" + "github.com/ethereum/go-ethereum/p2p/protocols" + "github.com/ethereum/go-ethereum/pot" + "github.com/ethereum/go-ethereum/rpc" +) + +func init() { + h := log.CallerFileHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(true))) + log.Root().SetHandler(h) +} + +func TestRunProtocol(t *testing.T) { + quitC := make(chan struct{}) + pss := newTestPss(nil) + ping := &pssPing{ + quitC: make(chan struct{}), + } + proto := newProtocol(ping) + _, err := baseTester(t, proto, pss, nil, nil, quitC) + if err != nil { + t.Fatalf(err.Error()) + } + quitC <- struct{}{} +} + +func TestIncoming(t *testing.T) { + quitC := make(chan struct{}) + pss := newTestPss(nil) + ctx, cancel := context.WithCancel(context.Background()) + var addr []byte + ping := &pssPing{ + quitC: make(chan struct{}), + } + proto := newProtocol(ping) + client, err := baseTester(t, proto, pss, ctx, cancel, quitC) + if err != nil { + t.Fatalf(err.Error()) + } + + client.ws.Call(&addr, "pss_baseAddr") + + code, _ := pssPingProtocol.GetCode(&pssPingMsg{}) + rlpbundle, err := newProtocolMsg(code, &pssPingMsg{ + Created: time.Now(), + }) + if err != nil { + t.Fatalf("couldn't make pssmsg") + } + + pssenv := PssEnvelope{ + From: addr, + Topic: NewTopic(proto.Name, int(proto.Version)), + TTL: DefaultTTL, + Payload: rlpbundle, + } + pssmsg := PssMsg{ + To: addr, + Payload: &pssenv, + } + + pss.Process(&pssmsg) + + go func() { + <-ping.quitC + client.cancel() + }() + + <-client.ctx.Done() + quitC <- struct{}{} +} + +func TestOutgoing(t *testing.T) { + quitC := make(chan struct{}) + pss := newTestPss(nil) + ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond * 250) + var addr []byte + var potaddr pot.Address + + ping := &pssPing{ + quitC: make(chan struct{}), + } + proto := newProtocol(ping) + client, err := baseTester(t, proto, pss, ctx, cancel, quitC) + if err != nil { + t.Fatalf(err.Error()) + } + + client.ws.Call(&addr, "pss_baseAddr") + copy(potaddr[:], addr) + + msg := &pssPingMsg{ + Created: time.Now(), + } + + topic := NewTopic(pssPingProtocol.Name, int(pssPingProtocol.Version)) + client.AddPssPeer(potaddr, pssPingProtocol) + nid, _ := discover.HexID("0x00") + p := p2p.NewPeer(nid, fmt.Sprintf("%v", potaddr), []p2p.Cap{}) + pp := protocols.NewPeer(p, client.peerPool[topic][potaddr], pssPingProtocol) + pp.Send(msg) + <-client.ctx.Done() + quitC <- struct{}{} +} + +func baseTester(t *testing.T, proto *p2p.Protocol, pss *Pss, ctx context.Context, cancel func(), quitC chan struct{}) (*PssClient, error) { + var err error + + client := newClient(t, pss, ctx, cancel, quitC) + + err = client.Start() + if err != nil { + return nil, err + } + + err = client.RunProtocol(proto, pssPingProtocol) + + if err != nil { + return nil, err + } + + return client, nil +} + +func newProtocol(ping *pssPing) *p2p.Protocol { + + return &p2p.Protocol{ + Name: pssPingProtocol.Name, + Version: pssPingProtocol.Version, + Length: 1, + Run: func(p *p2p.Peer, rw p2p.MsgReadWriter) error { + pp := protocols.NewPeer(p, rw, pssPingProtocol) + pp.Run(ping.pssPingHandler) + return nil + }, + } +} + +func newClient(t *testing.T, pss *Pss, ctx context.Context, cancel func(), quitC chan struct{}) *PssClient { + pssclient := NewPssClient(ctx, cancel, "", 0, false, "") + + srv := rpc.NewServer() + srv.RegisterName("pss", NewPssAPI(pss)) + ws := srv.WebsocketHandler([]string{"*"}) + uri := fmt.Sprintf("%s:%d", node.DefaultWSHost, node.DefaultWSPort) + + sock, err := net.Listen("tcp", uri) + if err != nil { + t.Fatalf("Tcp (recv) on %s failed: %v", uri, err) + } + + go func() { + http.Serve(sock, ws) + }() + + go func() { + <-quitC + sock.Close() + }() + return pssclient +} diff --git a/swarm/pss/common.go b/swarm/pss/common.go new file mode 100644 index 0000000000..7b985a6fea --- /dev/null +++ b/swarm/pss/common.go @@ -0,0 +1,66 @@ +package pss + +import ( + "io/ioutil" + "os" + "time" + + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/p2p/protocols" + "github.com/ethereum/go-ethereum/swarm/network" + "github.com/ethereum/go-ethereum/swarm/storage" +) + +type pssPingMsg struct { + Created time.Time +} + +type pssPing struct { + quitC chan struct{} +} + +func (self *pssPing) pssPingHandler(msg interface{}) error { + log.Warn("got ping", "msg", msg) + self.quitC <- struct{}{} + return nil +} + +var pssPingProtocol = &protocols.Spec{ + Name: "psstest", + Version: 1, + MaxMsgSize: 10 * 1024 * 1024, + Messages: []interface{}{ + pssPingMsg{}, + }, +} + +func newTestPss(addr []byte) *Pss { + if addr == nil { + addr = network.RandomAddr().OAddr + } + + // set up storage + cachedir, err := ioutil.TempDir("", "pss-cache") + if err != nil { + log.Error("create pss cache tmpdir failed", "error", err) + os.Exit(1) + } + dpa, err := storage.NewLocalDPA(cachedir) + if err != nil { + log.Error("local dpa creation failed", "error", err) + os.Exit(1) + } + + // set up routing + kp := network.NewKadParams() + kp.MinProxBinSize = 3 + + // create pss + pp := NewPssParams() + + overlay := network.NewKademlia(addr, kp) + ps := NewPss(overlay, dpa, pp) + + return ps +} + diff --git a/swarm/network/pss.go b/swarm/pss/pss.go similarity index 50% rename from swarm/network/pss.go rename to swarm/pss/pss.go index d713c1d764..dea193e89a 100644 --- a/swarm/network/pss.go +++ b/swarm/pss/pss.go @@ -1,4 +1,4 @@ -package network +package pss import ( "bytes" @@ -9,13 +9,16 @@ import ( "time" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/crypto/sha3" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/p2p" - "github.com/ethereum/go-ethereum/p2p/protocols" + "github.com/ethereum/go-ethereum/p2p/discover" "github.com/ethereum/go-ethereum/p2p/simulations/adapters" + "github.com/ethereum/go-ethereum/p2p/protocols" "github.com/ethereum/go-ethereum/pot" "github.com/ethereum/go-ethereum/rlp" + "github.com/ethereum/go-ethereum/rpc" + "github.com/ethereum/go-ethereum/swarm/network" "github.com/ethereum/go-ethereum/swarm/storage" ) @@ -25,17 +28,13 @@ const ( TopicResolverLength = 8 PssPeerCapacity = 256 PssPeerTopicDefaultCapacity = 8 - digestLength = 64 + digestLength = 32 digestCapacity = 256 defaultDigestCacheTTL = time.Second - pingTopicName = "pss" - pingTopicVersion = 1 ) var ( - errorNoForwarder = errors.New("no available forwarders in routing table") errorForwardToSelf = errors.New("forward to self") - errorBlockByCache = errors.New("message found in blocking cache") ) // Defines params for Pss @@ -51,50 +50,14 @@ func NewPssParams() *PssParams { } // Encapsulates the message transported over pss. -// -// Warning: do not access the To-member directly. Use *PssMsg.GetRecipient() and *PssMsg.SetRecipient() instead. type PssMsg struct { - // (we need the To-member exported for type inference) To []byte - Payload pssEnvelope -} - -// Retrieve the remote peer receipient address of the message -func (self *PssMsg) GetRecipient() []byte { - return self.To -} - -// Set the remote peer recipient address of the message -func (self *PssMsg) SetRecipient(to []byte) { - self.To = to + Payload *PssEnvelope } // String representation of PssMsg func (self *PssMsg) String() string { - return fmt.Sprintf("PssMsg: Recipient: %x", common.ByteLabel(self.GetRecipient())) -} - -// Pre-Whisper placeholder -type pssEnvelope struct { - Topic PssTopic - TTL uint16 - Payload []byte - SenderOAddr []byte - // SenderUAddr []byte -} - -// Pre-Whisper placeholder -type pssPayload struct { - Code uint64 - Size uint32 - Data []byte - ReceivedAt time.Time -} - -// Pre-Whisper placeholder -type pssCacheEntry struct { - expiresAt time.Time - receivedFrom []byte + return fmt.Sprintf("PssMsg: Recipient: %x", common.ByteLabel(self.To)) } // Topic defines the context of a message being transported over pss @@ -102,14 +65,70 @@ type pssCacheEntry struct { // Typically, one can map protocol handlers for the message payloads by mapping topic to them; see *Pss.Register() type PssTopic [TopicLength]byte -// Pre-Whisper placeholder -type pssDigest uint32 +func (self *PssTopic) String() string { + return fmt.Sprintf("%x", self) +} + +// Pre-Whisper placeholder, payload of PssMsg +type PssEnvelope struct { + Topic PssTopic + TTL uint16 + Payload []byte + From []byte +} + +// creates Pss envelope from sender address, topic and raw payload +func NewPssEnvelope(addr []byte, topic PssTopic, payload []byte) *PssEnvelope { + return &PssEnvelope{ + From: addr, + Topic: topic, + TTL: DefaultTTL, + Payload: payload, + } +} + + +func (msg *PssMsg) serialize() []byte { + rlpdata, _ := rlp.EncodeToBytes(msg) + /*buf := bytes.NewBuffer(nil) + buf.Write(self.PssEnvelope.Topic[:]) + buf.Write(self.PssEnvelope.Payload) + buf.Write(self.PssEnvelope.From) + return buf.Bytes()*/ + return rlpdata +} + + +var pssTransportProtocol = &protocols.Spec{ + Name: "pss", + Version: 1, + MaxMsgSize: 10 * 1024 * 1024, + Messages: []interface{}{ + PssMsg{}, + }, +} + +// encapsulates a protocol msg as PssEnvelope data +type PssProtocolMsg struct { + Code uint64 + Size uint32 + Payload []byte + ReceivedAt time.Time +} + +type pssCacheEntry struct { + expiresAt time.Time + receivedFrom []byte +} + +type pssDigest [digestLength]byte + +// Message handler func for a topic +type pssHandler func(msg []byte, p *p2p.Peer, from []byte) error // pss provides sending messages to nodes without having to be directly connected to them. // // The messages are wrapped in a PssMsg structure and routed using the swarm kademlia routing. -// The structure is used by normal incoming message handlers on the nodes to determine which action to take, forward or process. -// Thus it is up to the implementer to write a handler, and link the PssMsg to this appropriate handler. // // The top-level Pss object provides: // @@ -120,69 +139,117 @@ type pssDigest uint32 // - a dispatcher lookup, mapping protocols to topics // - a message cache to spot messages that previously have been forwarded type Pss struct { - Overlay // we can get the overlayaddress from this - - //peerPool map[pot.Address]map[PssTopic]*PssReadWriter // keep track of all virtual p2p.Peers we are currently speaking to - peerPool map[pot.Address]map[PssTopic]p2p.MsgReadWriter // keep track of all virtual p2p.Peers we are currently speaking to - handlers map[PssTopic]func([]byte, *p2p.Peer, []byte) error // topic and version based pss payload handlers - events map[PssTopic]*event.Feed // subscriptions for each topic - fwdcache map[pssDigest]pssCacheEntry // checksum of unique fields from pssmsg mapped to expiry, cache to determine whether to drop msg - cachettl time.Duration // how long to keep messages in fwdcache - hasher func(string) storage.Hasher // hasher to digest message to cache - baseAddr []byte + network.Overlay // we can get the overlayaddress from this + peerPool map[pot.Address]map[PssTopic]p2p.MsgReadWriter // keep track of all virtual p2p.Peers we are currently speaking to + handlers map[PssTopic]map[*pssHandler]bool // topic and version based pss payload handlers + fwdcache map[pssDigest]pssCacheEntry // checksum of unique fields from pssmsg mapped to expiry, cache to determine whether to drop msg + cachettl time.Duration // how long to keep messages in fwdcache lock sync.Mutex + dpa *storage.DPA } -func (self *Pss) hashMsg(msg *PssMsg) pssDigest { - hasher := self.hasher("SHA3")() - hasher.Reset() - hasher.Write(msg.GetRecipient()) - // hasher.Write(msg.Payload.SenderUAddr) - hasher.Write(msg.Payload.SenderOAddr) - hasher.Write(msg.Payload.Topic[:]) - hasher.Write(msg.Payload.Payload) - b := hasher.Sum([]byte{}) - return pssDigest(binary.BigEndian.Uint32(b)) +func (self *Pss) storeMsg(msg *PssMsg) (pssDigest, error) { + swg := &sync.WaitGroup{} + wwg := &sync.WaitGroup{} + buf := bytes.NewReader(msg.serialize()) + key, err := self.dpa.Store(buf, int64(buf.Len()), swg, wwg) + if err != nil { + log.Warn("Could not store in swarm", "err", err) + return pssDigest{}, err + } + log.Trace("Stored msg in swarm", "key", key) + digest := pssDigest{} + copy(digest[:], key[:digestLength]) + return digest, nil } // Creates a new Pss instance. A node should only need one of these // -// TODO error check overlay integrity -func NewPss(k Overlay, params *PssParams) *Pss { - baseAddr := k.BaseAddr() +// TODO: error check overlay integrity +func NewPss(k network.Overlay, dpa *storage.DPA, params *PssParams) *Pss { return &Pss{ Overlay: k, - //peerPool: make(map[pot.Address]map[PssTopic]*PssReadWriter, PssPeerCapacity), peerPool: make(map[pot.Address]map[PssTopic]p2p.MsgReadWriter, PssPeerCapacity), - handlers: make(map[PssTopic]func([]byte, *p2p.Peer, []byte) error), - events: make(map[PssTopic]*event.Feed), + handlers: make(map[PssTopic]map[*pssHandler]bool), fwdcache: make(map[pssDigest]pssCacheEntry), cachettl: params.Cachettl, - hasher: storage.MakeHashFunc, - baseAddr: baseAddr, + dpa: dpa, } } -func (p *Pss) Run(peer *bzzPeer) error { - return peer.Run(p.HandleMsg) -} - -func (p *Pss) HandleMsg(m interface{}) error { - msg, ok := m.(*PssMsg) - if !ok { - return fmt.Errorf("unknown pss protocol message type: %T", m) - } - _ = msg - // TODO: handle the message +func (self *Pss) Start(srv *p2p.Server) error { return nil } +func (self *Pss) Stop() error { + return nil +} + +func (self *Pss) Protocols() []p2p.Protocol { + return []p2p.Protocol{ + p2p.Protocol{ + Name: pssTransportProtocol.Name, + Version: pssTransportProtocol.Version, + Length: pssTransportProtocol.Length(), + Run: func(p *p2p.Peer, rw p2p.MsgReadWriter) error { + pp := protocols.NewPeer(p, rw, pssTransportProtocol) + pp.Run(self.handlePssMsg) + return nil + }, + }, + } +} + +func (self *Pss) APIs() []rpc.API { + return []rpc.API{ + rpc.API { + Namespace: "pss", + Version: "0.1", + Service: NewPssAPI(self), + Public: true, + }, + } +} + +// Takes the generated PssTopic of a protocol/chatroom etc, and links a handler function to it +// This allows the implementer to retrieve the right handler functions (invoke the right protocol) +// for an incoming message by inspecting the topic on it. +// a topic allows for multiple handlers +// returns a deregister function which needs to be called to deregister the handler +// (similar to event.Subscription.Unsubscribe()) +func (self *Pss) Register(topic PssTopic, handler pssHandler) func() { + self.lock.Lock() + defer self.lock.Unlock() + handlers := self.handlers[topic] + if handlers == nil { + handlers = make(map[*pssHandler]bool) + self.handlers[topic] = handlers + } + handlers[&handler] = true + return func() { self.deregister(topic, &handler) } +} + +func (self *Pss) deregister(topic PssTopic, h *pssHandler) { + self.lock.Lock() + defer self.lock.Unlock() + handlers := self.handlers[topic] + if len(handlers) == 1 { + delete(self.handlers, topic) + return + } + delete(handlers, h) +} + // enables to set address of node, to avoid backwards forwarding // // currently not in use as forwarder address is not known in the handler function hooked to the pss dispatcher. // it is included as a courtesy to custom transport layers that may want to implement this func (self *Pss) AddToCache(addr []byte, msg *PssMsg) error { - digest := self.hashMsg(msg) + //digest := self.hashMsg(msg) + digest, err := self.storeMsg(msg) + if err != nil { + return err + } return self.addFwdCacheSender(addr, digest) } @@ -228,35 +295,115 @@ func (self *Pss) checkFwdCache(addr []byte, digest pssDigest) bool { return false } -// Takes the generated PssTopic of a protocol, and links a handler function to it -// This allows the implementer to retrieve the right handler function (invoke the right protocol) for an incoming message by inspecting the topic on it. -func (self *Pss) Register(topic PssTopic, handler func(msg []byte, p *p2p.Peer, from []byte) error) error { - self.lock.Lock() - defer self.lock.Unlock() - self.handlers[topic] = func(msg []byte, p *p2p.Peer, from []byte) error { - self.alertSubscribers(&topic, msg) - return handler(msg, p, from) - } - self.registerFeed(topic) - return nil -} - -func (self *Pss) Subscribe(topic *PssTopic, ch chan []byte) (event.Subscription, error) { - _, ok := self.events[*topic] - if !ok { - return nil, fmt.Errorf("No feed registered for topic %v", topic) - } - sub := self.events[*topic].Subscribe(ch) - log.Trace("new pss subscribe", "topic", topic, "sub", sub) - return sub, nil -} - -func (self *Pss) GetHandler(topic PssTopic) func([]byte, *p2p.Peer, []byte) error { +func (self *Pss) getHandlers(topic PssTopic) map[*pssHandler]bool { self.lock.Lock() defer self.lock.Unlock() return self.handlers[topic] } +// +func (self *Pss) handlePssMsg(msg interface{}) error { + pssmsg := msg.(*PssMsg) + + if !self.isSelfRecipient(pssmsg) { + log.Trace("pss was for someone else :'( ... forwarding") + return self.Forward(pssmsg) + } + log.Trace("pss for us, yay! ... let's process!") + return self.Process(pssmsg) +} + +// processes a message with self as recipient +func (self *Pss) Process(pssmsg *PssMsg) error { + env := pssmsg.Payload + payload := env.Payload + handlers := self.getHandlers(env.Topic) + if len(handlers) == 0 { + return fmt.Errorf("No registered handler for topic '%s'", env.Topic) + } + nid, _ := discover.HexID("0x00") + p := p2p.NewPeer(nid, fmt.Sprintf("%x", env.From), []p2p.Cap{}) + for f := range handlers { + err := (*f)(payload, p, env.From) + if err != nil { + return err + } + } + return nil +} + +// Sends a message using pss. The message could be anything at all, and will be handled by whichever handler function is mapped to PssTopic using *Pss.Register() +// +// The to address is a swarm overlay address +func (self *Pss) Send(to []byte, topic PssTopic, msg []byte) error { + sender := self.Overlay.BaseAddr() + pssenv := NewPssEnvelope(sender, topic, msg) + pssmsg := &PssMsg{ + To: to, + Payload: pssenv, + } + return self.Forward(pssmsg) +} + +// Forwards a pss message to the peer(s) closest to the to address +// +// Handlers that want to pass on a message should call this directly +func (self *Pss) Forward(msg *PssMsg) error { + + if self.isSelfRecipient(msg) { + return errorForwardToSelf + } + + digest, err := self.storeMsg(msg) + if err != nil { + log.Warn(fmt.Sprintf("could not store message %v to cache: %v", msg, err)) + } + + if self.checkFwdCache(nil, digest) { + log.Trace(fmt.Sprintf("pss relay block-cache match: FROM %x TO %x", common.ByteLabel(self.Overlay.BaseAddr()), common.ByteLabel(msg.To))) + return nil + } + + // TODO:check integrity of message + sent := 0 + + // send with kademlia + // find the closest peer to the recipient and attempt to send + self.Overlay.EachConn(msg.To, 256, func(op network.OverlayConn, po int, isproxbin bool) bool { + p, ok := op.(network.Peer) + if !ok { + return true + } + addr := self.Overlay.BaseAddr() + sendMsg := fmt.Sprintf("%x: msg to %x via %x", common.ByteLabel(addr), common.ByteLabel(msg.To), common.ByteLabel(p.Over())) + if self.checkFwdCache(p.Over(), digest) { + log.Info(fmt.Sprintf("%v: peer already forwarded to", sendMsg)) + return true + } + err := p.Send(msg) + if err != nil { + log.Warn(fmt.Sprintf("%v: failed forwarding: %v", sendMsg, err)) + return true + } + log.Trace(fmt.Sprintf("%v: successfully forwarded", sendMsg)) + sent++ + // if equality holds, p is always the first peer given in the iterator + if bytes.Equal(msg.To, p.Over()) || !isproxbin { + return false + } + log.Trace(fmt.Sprintf("%x is in proxbin, keep forwarding", common.ByteLabel(p.Over()))) + return true + }) + + if sent == 0 { + log.Error("PSS: unable to forward to any peers") + return nil + } + + self.addFwdCacheExpire(digest) + return nil +} + // Links a pss peer address and topic to a dedicated p2p.MsgReadWriter in the pss peerpool, and runs the specificed protocol on this p2p.MsgReadWriter and the specified peer // // The effect is that now we have a "virtual" protocol running on an artificial p2p.Peer, which can be looked up and piped to through Pss using swarm overlay address and topic @@ -267,121 +414,32 @@ func (self *Pss) AddPeer(p *p2p.Peer, addr pot.Address, run adapters.RunProtocol go func() { err := run(p, rw) log.Warn(fmt.Sprintf("pss vprotocol quit on addr %v topic %v: %v", addr, topic, err)) + self.removePeerTopic(rw, topic) }() return nil } -// Removes a pss peer from the pss peerpool -func (self *Pss) RemovePeer(id pot.Address) { - self.lock.Lock() - defer self.lock.Unlock() - self.peerPool[id] = nil - return -} - func (self *Pss) addPeerTopic(id pot.Address, topic PssTopic, rw p2p.MsgReadWriter) error { - if self.peerPool[id][topic] == nil { + if self.peerPool[id] == nil { self.peerPool[id] = make(map[PssTopic]p2p.MsgReadWriter, PssPeerTopicDefaultCapacity) } self.peerPool[id][topic] = rw return nil } -func (self *Pss) removePeerTopic(id pot.Address, topic PssTopic) { - self.peerPool[id][topic] = nil - return +func (self *Pss) removePeerTopic(rw p2p.MsgReadWriter, topic PssTopic) { + prw, ok := rw.(*PssReadWriter) + if !ok { + return + } + delete(self.peerPool[prw.To], topic) + if len(self.peerPool[prw.To]) == 0 { + delete(self.peerPool, prw.To) + } } func (self *Pss) isActive(id pot.Address, topic PssTopic) bool { - if self.peerPool[id][topic] == nil { - return false - } - return true -} - -func (self *Pss) registerFeed(topic PssTopic) { - self.events[topic] = &event.Feed{} -} - -func (self *Pss) alertSubscribers(topic *PssTopic, msg []byte) error { - feed, ok := self.events[*topic] - if !ok { - return fmt.Errorf("No subscriptions registered for topic %v", topic) - } - numsent := feed.Send(msg) - log.Trace(fmt.Sprintf("pss sent to %d subscribers", numsent)) - return nil -} - -// Sends a message using pss. The message could be anything at all, and will be handled by whichever handler function is mapped to PssTopic using *Pss.Register() -// -// The to address is a swarm overlay address -func (self *Pss) Send(to []byte, topic PssTopic, msg []byte) error { - - pssenv := pssEnvelope{ - SenderOAddr: self.baseAddr, - // SenderUAddr: self.baseAddr.Under(), - Topic: topic, - TTL: DefaultTTL, - Payload: msg, - } - - pssmsg := &PssMsg{ - Payload: pssenv, - } - pssmsg.SetRecipient(to) - - return self.Forward(pssmsg) -} - -// Forwards a pss message to the peer(s) closest to the to address -// -// Handlers that want to pass on a message should call this directly -func (self *Pss) Forward(msg *PssMsg) error { - - if self.IsSelfRecipient(msg) { - return errorForwardToSelf - } - - digest := self.hashMsg(msg) - - if self.checkFwdCache(nil, digest) { - log.Trace(fmt.Sprintf("pss relay block-cache match: FROM %x TO %x", common.ByteLabel(self.baseAddr), common.ByteLabel(msg.GetRecipient()))) - //return errorBlockByCache - return nil - } - - // TODO:check integrity of message - - sent := 0 - - // send with kademlia - // find the closest peer to the recipient and attempt to send - self.Overlay.EachConn(msg.GetRecipient(), 256, func(p OverlayConn, po int, isproxbin bool) bool { - if self.checkFwdCache(p.Address(), digest) { - log.Warn(fmt.Sprintf("BOUNCE DEFER PSS-relay FROM %x TO %x THRU %x:", common.ByteLabel(self.baseAddr), common.ByteLabel(msg.GetRecipient()), common.ByteLabel(p.Address()))) - return true - } - log.Warn(fmt.Sprintf("Attempting PSS-relay FROM %x TO %x THRU %x", common.ByteLabel(self.baseAddr), common.ByteLabel(msg.GetRecipient()), common.ByteLabel(p.Address()))) - err := p.(Peer).Send(msg) - if err != nil { - log.Warn(fmt.Sprintf("FAILED PSS-relay FROM %x TO %x THRU %x: %v", common.ByteLabel(self.baseAddr), common.ByteLabel(msg.GetRecipient()), common.ByteLabel(p.Address()), err)) - return true - } - sent++ - if bytes.Equal(msg.GetRecipient(), p.Address()) || !isproxbin { - return false - } - log.Trace(fmt.Sprintf("%x is in proxbin, so we continue sending", common.ByteLabel(p.Address()))) - return true - }) - if sent == 0 { - return fmt.Errorf("PSS Was not able to send to any peers") - } else { - self.addFwdCacheExpire(digest) - } - - return nil + return self.peerPool[id][topic] != nil } // Convenience object that: @@ -392,19 +450,17 @@ func (self *Pss) Forward(msg *PssMsg) error { // Implements p2p.MsgReadWriter type PssReadWriter struct { *Pss - RecipientOAddr pot.Address - LastActive time.Time - rw chan p2p.Msg - spec *protocols.Spec - topic *PssTopic + To pot.Address + LastActive time.Time + rw chan p2p.Msg + spec *protocols.Spec + topic *PssTopic } // Implements p2p.MsgReader func (prw PssReadWriter) ReadMsg() (p2p.Msg, error) { msg := <-prw.rw - log.Trace(fmt.Sprintf("pssrw readmsg: %v", msg)) - return msg, nil } @@ -417,11 +473,11 @@ func (prw PssReadWriter) WriteMsg(msg p2p.Msg) error { } msg.Decode(ifc) - to := prw.RecipientOAddr.Bytes() - - pmsg, _ := makeMsg(msg.Code, ifc) - - return prw.Pss.Send(to, *prw.topic, pmsg) + pmsg, err := newProtocolMsg(msg.Code, ifc) + if err != nil { + return err + } + return prw.Pss.Send(prw.To.Bytes(), *prw.topic, pmsg) } // Injects a p2p.Msg into the MsgReadWriter, so that it appears on the associated p2p.MsgReader @@ -432,52 +488,40 @@ func (prw PssReadWriter) injectMsg(msg p2p.Msg) error { } // Convenience object for passing messages in and out of the p2p layer -type pssProtocol struct { +type PssProtocol struct { *Pss - virtualProtocol *p2p.Protocol + proto *p2p.Protocol topic *PssTopic - spec *protocols.Spec + spec *protocols.Spec } // Constructor -func NewPssProtocol(pss *Pss, topic *PssTopic, spec *protocols.Spec, targetprotocol *p2p.Protocol) *pssProtocol { - pp := &pssProtocol{ +func NewPssProtocol(pss *Pss, topic *PssTopic, spec *protocols.Spec, targetprotocol *p2p.Protocol) *PssProtocol { + pp := &PssProtocol{ Pss: pss, - virtualProtocol: targetprotocol, + proto: targetprotocol, topic: topic, - spec: spec, + spec: spec, } return pp } -// Retrieves a convenience method for passing an incoming message into the p2p layer -// -// If the implementer wishes to use the p2p.Protocol (or p2p/protocols) message handling, this handler can be directly registered as a handler for the PssMsg structure -func (self *pssProtocol) GetHandler() func([]byte, *p2p.Peer, []byte) error { - return self.handle -} - -func (self *pssProtocol) handle(msg []byte, p *p2p.Peer, senderAddr []byte) error { +func (self *PssProtocol) handle(msg []byte, p *p2p.Peer, senderAddr []byte) error { hashoaddr := pot.NewHashAddressFromBytes(senderAddr).Address if !self.isActive(hashoaddr, *self.topic) { rw := &PssReadWriter{ - Pss: self.Pss, - RecipientOAddr: hashoaddr, - rw: make(chan p2p.Msg), - spec: self.spec, - topic: self.topic, + Pss: self.Pss, + To: hashoaddr, + rw: make(chan p2p.Msg), + spec: self.spec, + topic: self.topic, } - self.Pss.AddPeer(p, hashoaddr, self.virtualProtocol.Run, *self.topic, rw) + self.Pss.AddPeer(p, hashoaddr, self.proto.Run, *self.topic, rw) } - payload := &pssPayload{} - rlp.DecodeBytes(msg, payload) - - pmsg := p2p.Msg{ - Code: payload.Code, - Size: uint32(len(payload.Data)), - ReceivedAt: time.Now(), - Payload: bytes.NewBuffer(payload.Data), + pmsg, err := ToP2pMsg(msg) + if err != nil { + return fmt.Errorf("could not decode pssmsg") } vrw := self.Pss.peerPool[hashoaddr][*self.topic].(*PssReadWriter) @@ -486,26 +530,11 @@ func (self *pssProtocol) handle(msg []byte, p *p2p.Peer, senderAddr []byte) erro return nil } -func (self *Pss) IsSelfRecipient(msg *PssMsg) bool { - if bytes.Equal(msg.GetRecipient(), self.baseAddr) { - return true - } - return false +func (self *Pss) isSelfRecipient(msg *PssMsg) bool { + return bytes.Equal(msg.To, self.Overlay.BaseAddr()) } -func (self *Pss) GetPingHandler() func([]byte, *p2p.Peer, []byte) error { - pingtopic, _ := MakeTopic(pingTopicName, pingTopicVersion) - return func(msg []byte, p *p2p.Peer, from []byte) error { - if bytes.Equal([]byte("ping"), msg) { - log.Trace(fmt.Sprintf("swarm pss ping from %x sending pong", common.ByteLabel(from))) - self.Send(from, pingtopic, []byte("pong")) - } - return nil - } -} - -// Pre-Whisper placeholder -func makeMsg(code uint64, msg interface{}) ([]byte, error) { +func newProtocolMsg(code uint64, msg interface{}) ([]byte, error) { rlpdata, err := rlp.EncodeToBytes(msg) if err != nil { @@ -515,30 +544,39 @@ func makeMsg(code uint64, msg interface{}) ([]byte, error) { // previous attempts corrupted nested structs in the payload iself upon deserializing // therefore we use two separate []byte fields instead of peerAddr // TODO verify that nested structs cannot be used in rlp - smsg := &pssPayload{ + smsg := &PssProtocolMsg{ Code: code, Size: uint32(len(rlpdata)), - Data: rlpdata, + Payload: rlpdata, } - rlpbundle, err := rlp.EncodeToBytes(smsg) - if err != nil { - return nil, err - } - - return rlpbundle, nil + return rlp.EncodeToBytes(smsg) } -// Compiles a new PssTopic from a given name and version. +// constructs a new PssTopic from a given name and version. // // Analogous to the name and version members of p2p.Protocol -func MakeTopic(s string, v int) (PssTopic, error) { - t := [TopicLength]byte{} - if len(s)+4 <= TopicLength { - copy(t[4:len(s)+4], s) - } else { - return t, fmt.Errorf("topic '%t' too long", s) - } - binary.PutVarint(t[:4], int64(v)) - return t, nil +func NewTopic(s string, v int) (topic PssTopic) { + h := sha3.NewKeccak256() + h.Write([]byte(s)) + buf := make([]byte, TopicLength / 8) + binary.PutUvarint(buf, uint64(v)) + h.Write(buf) + copy(topic[:], h.Sum(buf)[:]) + return topic +} + + +func ToP2pMsg(msg []byte) (p2p.Msg, error) { + payload := &PssProtocolMsg{} + if err := rlp.DecodeBytes(msg, payload); err != nil { + return p2p.Msg{}, fmt.Errorf("pss protocol handler unable to decode payload as p2p message: %v", err) + } + + return p2p.Msg{ + Code: payload.Code, + Size: uint32(len(payload.Payload)), + ReceivedAt: time.Now(), + Payload: bytes.NewBuffer(payload.Payload), + }, nil } diff --git a/swarm/network/pss_test.go b/swarm/pss/pss_test.go similarity index 73% rename from swarm/network/pss_test.go rename to swarm/pss/pss_test.go index 3c059ac972..d7c371ad5e 100644 --- a/swarm/network/pss_test.go +++ b/swarm/pss/pss_test.go @@ -1,23 +1,399 @@ -package network +package pss import ( + "bytes" + "context" + "encoding/hex" + "fmt" + "io/ioutil" "os" - + "testing" + "time" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/node" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/p2p/simulations" + "github.com/ethereum/go-ethereum/p2p/simulations/adapters" + p2ptest "github.com/ethereum/go-ethereum/p2p/testing" + "github.com/ethereum/go-ethereum/swarm/network" + "github.com/ethereum/go-ethereum/swarm/storage" ) const ( - protocolName = "foo" - protocolVersion = 42 + pssServiceName = "pss" + bzzServiceName = "bzz" ) +var topic PssTopic = NewTopic(pssPingProtocol.Name, int(pssPingProtocol.Version)) + +var services = newServices() + func init() { +<<<<<<< HEAD:swarm/network/pss_test.go h := log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(true))) // // h := log.CallerFileHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(true))) +======= + adapters.RegisterServices(services) + h := log.CallerFileHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(true))) +>>>>>>> 9c47957... swarm, swarm/pss, swarm/network: pssclient rw reads and writes from websocket:swarm/pss/pss_test.go log.Root().SetHandler(h) } +func TestPssCache(t *testing.T) { + var err error + to, _ := hex.DecodeString("08090a0b0c0d0e0f1011121314150001020304050607161718191a1b1c1d1e1f") + oaddr, _ := hex.DecodeString("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f") + //uaddr, _ := hex.DecodeString("101112131415161718191a1b1c1d1e1f000102030405060708090a0b0c0d0e0f") + //proofbytes := []byte{241, 172, 117, 105, 88, 154, 82, 33, 176, 188, 91, 244, 245, 85, 86, 16, 120, 232, 70, 45, 182, 188, 99, 103, 157, 3, 202, 121, 252, 21, 129, 22} + proofbytes, _ := hex.DecodeString("ad312dca94df401555cfdeb85a6a1f87fb8f240f08dc36af246bd9d4d41efd89") + ps := newTestPss(oaddr) + pp := NewPssParams() + data := []byte("foo") + datatwo := []byte("bar") + fwdaddr := network.RandomAddr() + msg := &PssMsg{ + Payload: &PssEnvelope{ + TTL: 0, + From: oaddr, + Topic: topic, + Payload: data, + }, + To: to, + } + + msgtwo := &PssMsg{ + Payload: &PssEnvelope{ + TTL: 0, + From: oaddr, + Topic: topic, + Payload: datatwo, + }, + To: to, + } + + digest, err := ps.storeMsg(msg) + if err != nil { + t.Fatalf("could not store cache msgone: %v", err) + } + digesttwo, err := ps.storeMsg(msgtwo) + if err != nil { + t.Fatalf("could not store cache msgtwo: %v", err) + } + + if !bytes.Equal(digest[:], proofbytes) { + t.Fatalf("digest - got: %x, expected: %x", digest, proofbytes) + } + + if digest == digesttwo { + t.Fatalf("different msgs return same crc: %d", digesttwo) + } + + // check the sender cache + err = ps.addFwdCacheSender(fwdaddr.Over(), digest) + if err != nil { + t.Fatalf("write to pss sender cache failed: %v", err) + } + + if !ps.checkFwdCache(fwdaddr.Over(), digest) { + t.Fatalf("message %v should have SENDER record in cache but checkCache returned false", msg) + } + + if ps.checkFwdCache(fwdaddr.Over(), digesttwo) { + t.Fatalf("message %v should NOT have SENDER record in cache but checkCache returned true", msgtwo) + } + + // check the expire cache + err = ps.addFwdCacheExpire(digest) + if err != nil { + t.Fatalf("write to pss expire cache failed: %v", err) + } + + if !ps.checkFwdCache(nil, digest) { + t.Fatalf("message %v should have EXPIRE record in cache but checkCache returned false", msg) + } + + if ps.checkFwdCache(nil, digesttwo) { + t.Fatalf("message %v should NOT have EXPIRE record in cache but checkCache returned true", msgtwo) + } + + time.Sleep(pp.Cachettl) + if ps.checkFwdCache(nil, digest) { + t.Fatalf("message %v should have expired from cache but checkCache returned true", msg) + } + + err = ps.AddToCache(fwdaddr.Over(), msgtwo) + if err != nil { + t.Fatalf("public accessor cache write failed: %v", err) + } + + if !ps.checkFwdCache(fwdaddr.Over(), digesttwo) { + t.Fatalf("message %v should have SENDER record in cache but checkCache returned false", msgtwo) + } +} + +func TestPssRegisterHandler(t *testing.T) { + var err error + addr := network.RandomAddr() + ps := newTestPss(addr.OAddr) + from := network.RandomAddr() + payload := []byte("payload") + topic := NewTopic(pssTransportProtocol.Name, int(pssTransportProtocol.Version)) + wrongtopic := NewTopic("foo", 42) + checkMsg := func(msg []byte, p *p2p.Peer, sender []byte) error { + if !bytes.Equal(from.OAddr, sender) { + return fmt.Errorf("sender mismatch. expected %x, got %x", from.OAddr, sender) + } + if !bytes.Equal(msg, payload) { + return fmt.Errorf("sender mismatch. expected %x, got %x", msg, payload) + } + return nil + } + deregister := ps.Register(topic, checkMsg) + pssmsg := &PssMsg{Payload: NewPssEnvelope(from.OAddr, topic, payload)} + err = ps.Process(pssmsg) + if err != nil { + t.Fatal(err) + } + var i int + err = ps.Process(&PssMsg{Payload: NewPssEnvelope(from.OAddr, wrongtopic, payload)}) + expErr := "" + if err == nil || err.Error() == expErr { + t.Fatalf("unhandled topic expected '%v', got '%v'", expErr, err) + } + deregister2 := ps.Register(topic, func(msg []byte, p *p2p.Peer, sender []byte) error { i++; return nil }) + err = ps.Process(pssmsg) + if err != nil { + t.Fatal(err) + } + if i != 1 { + t.Fatalf("second registerer handler did not run") + } + deregister() + deregister2() + err = ps.Process(&PssMsg{Payload: NewPssEnvelope(from.OAddr, topic, payload)}) + expErr = "" + if err == nil || err.Error() == expErr { + t.Fatalf("reregister handler expected %v, got %v", expErr, err) + } +} + +func TestPssSimpleLinear(t *testing.T) { + nodeconfig := adapters.RandomNodeConfig() + addr := network.NewAddrFromNodeId(nodeconfig.Id) + pss := newTestPss(addr.OAddr) + + pt := p2ptest.NewProtocolTester(t, nodeconfig.Id, 2, pss.Protocols()[0].Run) + + return []p2ptest.Exchange{ + p2ptest.Exchange{ + Expects: []p2ptest.Expect{ + p2ptest.Expect{ + Code: 0, + Msg: lhs, + Peer: id, + }, + }, + Triggers: []p2ptest.Trigger{ + p2ptest.Trigger{ + Code: 0, + Msg: , + Peer: id, + }, + }, + }, + } +} + + +func TestPssFullRandom10_5_5(t *testing.T) { + adapter := adapters.NewSimAdapter(services) + testPssFullRandom(t, adapter, 10, 5, 5) +} + +func testPssFullRandom(t *testing.T, adapter adapters.NodeAdapter, nodecount int, fullnodecount int, msgcount int) { + var lastid *adapters.NodeId = nil + nodeCount := 5 + net := simulations.NewNetwork(adapter, &simulations.NetworkConfig{ + Id: "0", + DefaultService: bzzServiceName, + }) + defer net.Shutdown() + + trigger := make(chan *adapters.NodeId) + ids := make([]*adapters.NodeId, nodeCount) + + for i := 0; i < nodeCount; i++ { + nodeconfig := adapters.RandomNodeConfig() + nodeconfig.Services = []string{"bzz", "pss"} + node, err := net.NewNodeWithConfig(nodeconfig) + if err != nil { + t.Fatalf("error starting node: %s", err) + } + + if err := net.Start(node.ID()); err != nil { + t.Fatalf("error starting node %s: %s", node.ID().Label(), err) + } + + if err := triggerChecks(trigger, net, node.ID()); err != nil { + t.Fatal("error triggering checks for node %s: %s", node.ID().Label(), err) + } + ids[i] = node.ID() + } + + // run a simulation which connects the 10 nodes in a ring and waits + // for full peer discovery + action := func(ctx context.Context) error { + for i, id := range ids { + var peerId *adapters.NodeId + if i == 0 { + peerId = ids[len(ids)-1] + } else { + peerId = ids[i-1] + } + if err := net.Connect(id, peerId); err != nil { + return err + } + } + return nil + } + check := func(ctx context.Context, id *adapters.NodeId) (bool, error) { + select { + case <-ctx.Done(): + return false, ctx.Err() + default: + } + + node := net.GetNode(id) + if node == nil { + return false, fmt.Errorf("unknown node: %s", id) + } + client, err := node.Client() + if err != nil { + return false, fmt.Errorf("error getting node client: %s", err) + } + + log.Debug("in check", "node", id) + + if lastid != nil { + //msg := pssPingMsg{Created: time.Now(),} + client.CallContext(context.Background(), nil, "pss_sendRaw", topic, PssAPIMsg{ + Addr: lastid.Bytes(), + Msg: []byte{1,2,3}, + }) + } + lastid = id + + return true, nil + } + + timeout := 5 * time.Second + ctx, cancel := context.WithTimeout(context.Background(), timeout) + defer cancel() + + result := simulations.NewSimulation(net).Run(ctx, &simulations.Step{ + Action: action, + Trigger: trigger, + Expect: &simulations.Expectation{ + Nodes: ids, + Check: check, + }, + }) + if result.Error != nil { + t.Fatalf("simulation failed: %s", result.Error) + } + + t.Log("Simulation Passed:") + t.Logf("Duration: %s", result.FinishedAt.Sub(result.StartedAt)) + + time.Sleep(time.Second * 2) +} + +// triggerChecks triggers a simulation step check whenever a peer is added or +// removed from the given node +func triggerChecks(trigger chan *adapters.NodeId, net *simulations.Network, id *adapters.NodeId) error { + node := net.GetNode(id) + if node == nil { + return fmt.Errorf("unknown node: %s", id) + } + client, err := node.Client() + if err != nil { + return err + } + events := make(chan PssAPIMsg) + sub, err := client.Subscribe(context.Background(), "pss", events, "newMsg", topic) + if err != nil { + return fmt.Errorf("error getting peer events for node %v: %s", id, err) + } + go func() { + defer sub.Unsubscribe() + for { + select { + case msg := <-events: + log.Warn("pss rpc got msg", "msg", msg) + trigger <- id + case err := <-sub.Err(): + if err != nil { + log.Error(fmt.Sprintf("error getting peer events for node %v", id), "err", err) + } + return + } + } + }() + return nil +} + +func newServices() adapters.Services { + + bzzs := make(map[*adapters.NodeId]*network.Bzz) + + adaptersservices := make(map[string]adapters.ServiceFunc) + + adaptersservices["bzz"] = func(id *adapters.NodeId) node.Service { + // setup hive + addr := network.NewAddrFromNodeId(id) + + config := &network.BzzConfig{ + OverlayAddr: addr.Over(), + UnderlayAddr: addr.Under(), + KadParams: network.NewKadParams(), + HiveParams: network.NewHiveParams(), + } + + config.KadParams.MinProxBinSize = 2 + config.KadParams.MaxBinSize = 3 + config.KadParams.MinBinSize = 1 + config.KadParams.MaxRetries = 1000 + config.KadParams.RetryExponent = 2 + config.KadParams.RetryInterval = 1000000 + + config.HiveParams.KeepAliveInterval = time.Second + + bzzs[id] = network.NewBzz(config) + + return bzzs[id] + } + + adaptersservices["pss"] = func(id *adapters.NodeId) node.Service { + // pss setup + cachedir, err := ioutil.TempDir("", "pss-cache") + if err != nil { + log.Error("create pss cache tmpdir failed", "error", err) + return nil + } + dpa, err := storage.NewLocalDPA(cachedir) + if err != nil { + log.Error("local dpa creation failed", "error", err) + return nil + } + pssp := NewPssParams() + return NewPss(bzzs[id].Kademlia, dpa, pssp) + } + + return adaptersservices +} + /* // example protocol implementation peer // message handlers are methods of this @@ -127,143 +503,10 @@ func (self *pssTestService) Run(peer *bzzPeer) error { defer self.node.Remove(peer) return peer.Run(self.msgFunc) } +*/ -func TestPssCache(t *testing.T) { - var err error - to, _ := hex.DecodeString("08090a0b0c0d0e0f1011121314150001020304050607161718191a1b1c1d1e1f") - oaddr, _ := hex.DecodeString("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f") - uaddr, _ := hex.DecodeString("101112131415161718191a1b1c1d1e1f000102030405060708090a0b0c0d0e0f") - ps := makePss(oaddr) - pp := NewPssParams() - topic, _ := MakeTopic(protocolName, protocolVersion) - data := []byte("foo") - fwdaddr := RandomAddr() - msg := &PssMsg{ - Payload: pssEnvelope{ - TTL: 0, - SenderOAddr: oaddr, - // SenderUAddr: uaddr, - Topic: topic, - Payload: data, - }, - } - msg.SetRecipient(to) +/* - msgtwo := &PssMsg{ - Payload: pssEnvelope{ - TTL: 0, - SenderOAddr: oaddr, - // SenderUAddr: oaddr, - Topic: topic, - Payload: data, - }, - } - msgtwo.SetRecipient(to) - - digest := ps.hashMsg(msg) - digesttwo := ps.hashMsg(msgtwo) - - if digest != 3595343914 { - t.Fatalf("digest - got: %d, expected: %d", digest, 3595343914) - } - - if digest == digesttwo { - t.Fatalf("different msgs return same crc: %d", digesttwo) - } - - // check the sender cache - err = ps.addFwdCacheSender(fwdaddr.Over(), digest) - if err != nil { - t.Fatalf("write to pss sender cache failed: %v", err) - } - - if !ps.checkFwdCache(fwdaddr.Over(), digest) { - t.Fatalf("message %v should have SENDER record in cache but checkCache returned false", msg) - } - - if ps.checkFwdCache(fwdaddr.Over(), digesttwo) { - t.Fatalf("message %v should NOT have SENDER record in cache but checkCache returned true", msgtwo) - } - - // check the expire cache - err = ps.addFwdCacheExpire(digest) - if err != nil { - t.Fatalf("write to pss expire cache failed: %v", err) - } - - if !ps.checkFwdCache(nil, digest) { - t.Fatalf("message %v should have EXPIRE record in cache but checkCache returned false", msg) - } - - if ps.checkFwdCache(nil, digesttwo) { - t.Fatalf("message %v should NOT have EXPIRE record in cache but checkCache returned true", msgtwo) - } - - time.Sleep(pp.Cachettl) - if ps.checkFwdCache(nil, digest) { - t.Fatalf("message %v should have expired from cache but checkCache returned true", msg) - } - - err = ps.AddToCache(fwdaddr.Over(), msgtwo) - if err != nil { - t.Fatalf("public accessor cache write failed: %v", err) - } - - if !ps.checkFwdCache(fwdaddr.Over(), digesttwo) { - t.Fatalf("message %v should have SENDER record in cache but checkCache returned false", msgtwo) - } -} - -func TestPssRegisterHandler(t *testing.T) { - var topic PssTopic - var err error - addr := RandomAddr() - ps := makePss(addr.Under()) - - topic, _ = MakeTopic(protocolName, protocolVersion) - err = ps.Register(topic, func(msg []byte, p *p2p.Peer, sender []byte) error { return nil }) - if err != nil { - t.Fatalf("couldnt register protocol 'foo' v 42: %v", err) - } - - topic, _ = MakeTopic(protocolName, protocolVersion) - err = ps.Register(topic, func(msg []byte, p *p2p.Peer, sender []byte) error { return nil }) - if err == nil { - t.Fatalf("register protocol 'abc..789' v 65536 should have failed") - } -} - -func TestPssFullRandom10_10_5(t *testing.T) { - testPssFullRandom(t, 10, 10, 5) -} - -func TestPssFullRandom50_50_5(t *testing.T) { - testPssFullRandom(t, 50, 50, 5) -} - -func TestPssFullRandom50_50_25(t *testing.T) { - testPssFullRandom(t, 50, 50, 25) -} - -func TestPssFullRandom10_100_50(t *testing.T) { - testPssFullRandom(t, 10, 100, 50) -} - -func TestPssFullRandom50_100_50(t *testing.T) { - testPssFullRandom(t, 50, 100, 50) -} - -func TestPssFullRandom100_100_5(t *testing.T) { - testPssFullRandom(t, 100, 100, 5) -} - -func TestPssFullRandom100_100_25(t *testing.T) { - testPssFullRandom(t, 100, 100, 25) -} - -func TestPssFullRandom100_100_50(t *testing.T) { - testPssFullRandom(t, 100, 100, 50) -} func testPssFullRandom(t *testing.T, numsends int, numnodes int, numfullnodes int) { var action func(ctx context.Context) error @@ -970,13 +1213,73 @@ func newPssSimulationTester(t *testing.T, numnodes int, numfullnodes int, trigge } func makePss(addr []byte) *Pss { - kp := NewKadParams() + + // set up storage + cachedir, err := ioutil.TempDir("", "pss-cache") + if err != nil { + log.Error("create pss cache tmpdir failed", "error", err) + os.Exit(1) + } + + dpa, err := storage.NewLocalDPA(cachedir) + if err != nil { + log.Error("local dpa creation failed", "error", err) + os.Exit(1) + } + // cannot use pyramidchunker as it still lacks joinfunc TestPssRegisterHandler(t *testing.T) { + addr := RandomAddr() + ps := newTestPss(addr.UnderlayAddr()) + from := RandomAddr() + payload := []byte("payload") + topic := NewTopic(protocolName, protocolVersion) + checkMsg := func(msg []byte, p *p2p.Peer, sender []byte) error { + if !bytes.Equal(from.OverlayAddr(), sender) { + return fmt.Errorf("sender mismatch. expected %x, got %x", from.OverlayAddr(), sender) + } + if !bytes.Equal(msg, payload) { + return fmt.Errorf("sender mismatch. expected %x, got %x", msg, payload) + } + if !bytes.Equal(from.UnderlayAddr(), p.ID()) { + return fmt.Errorf("sender mismatch. expected %x, got %x", from.UnderlayAddr(), p.ID()) + } + } + deregister := ps.Register(topic, checkMsg) + pssmsg := &PssMsg{Data: NewPssEnvelope(from, topic, payload)} + err = ps.Process(pssmsg) + if err != nil { + t.Fatal(err) + } + var i int + err = ps.Process(&PssMsg{Data: NewPssEnvelope(from, []byte("topic"), payload)}) + expErr := "" + if err == nil || err.Error() != expErr { + t.Fatalf("unhandled topic expected %v, got %v", expErr, err) + } + deregister2 := ps.Register(topic, func(msg []byte, p *p2p.Peer, sender []byte) error { i++; return nil }) + ps.Process(pssmsg) + if err != nil { + t.Fatal(err) + } + if i != 1 { + t.Fatalf("second registerer handler did not run") + } + deregister() + deregister2() + err = ps.Process(&PssMsg{Data: NewPssEnvelope(from, topic, payload)}) + expErr = "" + if err == nil || err.Error() != expErr { + t.Fatalf("reregister handler expected %v, got %v", expErr, err) + } +} + // dpa.Chunker = storage.NewPyramidChunker(storage.NewChunkerParams()) + + kp := network.NewKadParams() kp.MinProxBinSize = 3 pp := NewPssParams() - overlay := NewKademlia(addr, kp) - ps := NewPss(overlay, pp) + overlay := network.NewKademlia(addr, kp) + ps := NewPss(overlay, dpa, pp) //overlay.Prune(time.Tick(time.Millisecond * 250)) return ps } diff --git a/swarm/pss/pssapi.go b/swarm/pss/pssapi.go new file mode 100644 index 0000000000..2943d8d9ea --- /dev/null +++ b/swarm/pss/pssapi.go @@ -0,0 +1,76 @@ +package pss + +import ( + "context" + "fmt" + + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/rpc" +) + +// PssAPI is the RPC API module for Pss +type PssAPI struct { + *Pss +} + +// NewPssAPI constructs a PssAPI instance +func NewPssAPI(ps *Pss) *PssAPI { + return &PssAPI{Pss: ps} +} + +// PssAPIMsg is the type for messages, it extends the rlp encoded protocol Msg +// with the Sender's overlay address +type PssAPIMsg struct { + Msg []byte + Addr []byte +} + +// NewMsg API endpoint creates an RPC subscription +func (pssapi *PssAPI) NewMsg(ctx context.Context, topic PssTopic) (*rpc.Subscription, error) { + notifier, supported := rpc.NotifierFromContext(ctx) + if !supported { + return nil, fmt.Errorf("Subscribe not supported") + } + + psssub := notifier.CreateSubscription() + handler := func(msg []byte, p *p2p.Peer, from []byte) error { + apimsg := &PssAPIMsg{ + Msg: msg, + Addr: from, + } + if err := notifier.Notify(psssub.ID, apimsg); err != nil { + log.Warn(fmt.Sprintf("notification on pss sub topic %v rpc (sub %v) msg %v failed!", topic, psssub.ID, msg)) + } + return nil + } + deregf := pssapi.Pss.Register(topic, handler) + + go func() { + defer deregf() + //defer psssub.Unsubscribe() + select { + case err := <-psssub.Err(): + log.Warn(fmt.Sprintf("caught subscription error in pss sub topic: %v", topic, err)) + case <-notifier.Closed(): + log.Warn(fmt.Sprintf("rpc sub notifier closed")) + } + }() + + return psssub, nil +} + +// SendRaw sends the message (serialised into byte slice) to a peer with topic +func (pssapi *PssAPI) SendRaw(topic PssTopic, msg PssAPIMsg) error { + err := pssapi.Pss.Send(msg.Addr, topic, msg.Msg) + if err != nil { + return fmt.Errorf("send error: %v", err) + } + return fmt.Errorf("ok sent") +} + +// BaseAddr gets our own overlayaddress +func (pssapi *PssAPI) BaseAddr() ([]byte, error) { + log.Warn("inside baseaddr") + return pssapi.Pss.Overlay.BaseAddr(), nil +} diff --git a/swarm/swarm.go b/swarm/swarm.go index 3bad8b8e1e..6676a4e944 100644 --- a/swarm/swarm.go +++ b/swarm/swarm.go @@ -38,6 +38,7 @@ import ( "github.com/ethereum/go-ethereum/swarm/fuse" "github.com/ethereum/go-ethereum/swarm/network" "github.com/ethereum/go-ethereum/swarm/storage" + "github.com/ethereum/go-ethereum/swarm/pss" ) // the swarm stack From dedc26bd2cbed9aacc57de0602c1617935605696 Mon Sep 17 00:00:00 2001 From: nolash Date: Wed, 17 May 2017 08:45:52 +0200 Subject: [PATCH 19/24] swarm/pss, p2p/simulations/adapters: Multiple nodeadapter svcs + psstests --- p2p/simulations/adapters/inproc.go | 48 ++++++++++++++++++------------ swarm/pss/pss.go | 3 +- swarm/pss/pss_test.go | 22 +++++++------- 3 files changed, 43 insertions(+), 30 deletions(-) diff --git a/p2p/simulations/adapters/inproc.go b/p2p/simulations/adapters/inproc.go index 5c1c38aec4..0ca06ad0e4 100644 --- a/p2p/simulations/adapters/inproc.go +++ b/p2p/simulations/adapters/inproc.go @@ -87,7 +87,7 @@ func (s *SimAdapter) NewNode(config *NodeConfig) (Node, error) { //} //service := serviceFunc(id) - n, err := node.New(&node.Config{ + _, err := node.New(&node.Config{ P2P: p2p.Config{ PrivateKey: config.PrivateKey, MaxPeers: math.MaxInt32, @@ -101,27 +101,28 @@ func (s *SimAdapter) NewNode(config *NodeConfig) (Node, error) { return nil, err } - services := make(map[string]node.Service) + servicefuncs := make(map[string]ServiceFunc) for name, servicefunc := range s.services { - service := servicefunc(id) - if err := n.Register(func(ctx *node.ServiceContext) (node.Service, error) { + service := servicefunc(id, nil) + /*if err := n.Register(func(ctx *node.ServiceContext) (node.Service, error) { return service, err }); err != nil { return nil, err - } + }*/ for _, proto := range service.Protocols() { nodeprotos = append(nodeprotos, proto) } - services[name] = service + servicefuncs[name] = servicefunc } simnode := &SimNode{ - Node: n, + //node: n, Id: id, - services: services, + serviceFuncs: servicefuncs, adapter: s, config: config, + running: make(map[string]node.Service), } s.nodes[id.NodeID] = simnode return simnode, nil @@ -160,9 +161,9 @@ type SimNode struct { Id *NodeId config *NodeConfig adapter *SimAdapter - services map[string]node.Service + serviceFuncs map[string]ServiceFunc node *node.Node - running node.Service + running map[string]node.Service client *rpc.Client rpcMux *rpcMux } @@ -220,11 +221,16 @@ func (self *SimNode) Start(snapshot []byte) error { if self.node != nil { return errors.New("node already started") } - - newService := func(ctx *node.ServiceContext) (node.Service, error) { - service := self.serviceFunc(self.Id, snapshot) - self.running = service - return service, nil + + services := []node.ServiceConstructor{} + + for name, servicefunc := range self.serviceFuncs { + service := servicefunc(self.Id, snapshot) + + services = append(services, func(ctx *node.ServiceContext) (node.Service, error) { + self.running[name] = service + return service, nil + }) } node, err := node.New(&node.Config{ @@ -240,9 +246,12 @@ func (self *SimNode) Start(snapshot []byte) error { if err != nil { return err } - - if err := node.Register(newService); err != nil { - return err + + for _, service := range services { + log.Debug("registering service", "service", service) + if err := node.Register(service); err != nil { + return err + } } if err := node.Start(); err != nil { @@ -287,11 +296,12 @@ func (self *SimNode) Server() *p2p.Server { return nil } return self.node.Server() +} // Service returns a underlying node.Service of the speficied type func (self *SimNode) GetService(servicename string) node.Service { log.Warn("retrieving service", "name", servicename) - return self.services[servicename] + return self.running[servicename] } func (self *SimNode) SubscribeEvents(ch chan *p2p.PeerEvent) event.Subscription { diff --git a/swarm/pss/pss.go b/swarm/pss/pss.go index dea193e89a..f3369463bd 100644 --- a/swarm/pss/pss.go +++ b/swarm/pss/pss.go @@ -193,7 +193,8 @@ func (self *Pss) Protocols() []p2p.Protocol { Length: pssTransportProtocol.Length(), Run: func(p *p2p.Peer, rw p2p.MsgReadWriter) error { pp := protocols.NewPeer(p, rw, pssTransportProtocol) - pp.Run(self.handlePssMsg) + err := pp.Run(self.handlePssMsg) + log.Warn("pss protocol peer returned", "peer", p, "err", err) return nil }, }, diff --git a/swarm/pss/pss_test.go b/swarm/pss/pss_test.go index d7c371ad5e..02921c329f 100644 --- a/swarm/pss/pss_test.go +++ b/swarm/pss/pss_test.go @@ -30,14 +30,8 @@ var topic PssTopic = NewTopic(pssPingProtocol.Name, int(pssPingProtocol.Version) var services = newServices() func init() { -<<<<<<< HEAD:swarm/network/pss_test.go - h := log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(true))) - // - // h := log.CallerFileHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(true))) -======= adapters.RegisterServices(services) h := log.CallerFileHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(true))) ->>>>>>> 9c47957... swarm, swarm/pss, swarm/network: pssclient rw reads and writes from websocket:swarm/pss/pss_test.go log.Root().SetHandler(h) } @@ -185,7 +179,7 @@ func TestPssSimpleLinear(t *testing.T) { pss := newTestPss(addr.OAddr) pt := p2ptest.NewProtocolTester(t, nodeconfig.Id, 2, pss.Protocols()[0].Run) - + /* return []p2ptest.Exchange{ p2ptest.Exchange{ Expects: []p2ptest.Expect{ @@ -203,7 +197,9 @@ func TestPssSimpleLinear(t *testing.T) { }, }, }, - } + }*/ + + _ = pt } @@ -317,6 +313,11 @@ func triggerChecks(trigger chan *adapters.NodeId, net *simulations.Network, id * if node == nil { return fmt.Errorf("unknown node: %s", id) } + go func(){ + time.Sleep(time.Second) + trigger <- id + }() + /* client, err := node.Client() if err != nil { return err @@ -341,6 +342,7 @@ func triggerChecks(trigger chan *adapters.NodeId, net *simulations.Network, id * } } }() + */ return nil } @@ -350,7 +352,7 @@ func newServices() adapters.Services { adaptersservices := make(map[string]adapters.ServiceFunc) - adaptersservices["bzz"] = func(id *adapters.NodeId) node.Service { + adaptersservices["bzz"] = func(id *adapters.NodeId, snapshot []byte) node.Service { // setup hive addr := network.NewAddrFromNodeId(id) @@ -375,7 +377,7 @@ func newServices() adapters.Services { return bzzs[id] } - adaptersservices["pss"] = func(id *adapters.NodeId) node.Service { + adaptersservices["pss"] = func(id *adapters.NodeId, snapshot []byte) node.Service { // pss setup cachedir, err := ioutil.TempDir("", "pss-cache") if err != nil { From 3a4aee8087da67f265ce2aaf7afa07287d9657f7 Mon Sep 17 00:00:00 2001 From: nolash Date: Wed, 17 May 2017 16:45:42 +0200 Subject: [PATCH 20/24] swarm/pss, p2p/testing, p2p/simulations/adapters: svcs sequence --- p2p/simulations/adapters/inproc.go | 8 ++-- p2p/testing/protocoltester.go | 11 +++++- swarm/pss/common.go | 23 ++++++++++++ swarm/pss/pss_test.go | 59 ++++++++---------------------- 4 files changed, 53 insertions(+), 48 deletions(-) diff --git a/p2p/simulations/adapters/inproc.go b/p2p/simulations/adapters/inproc.go index 0ca06ad0e4..2acf3d0e90 100644 --- a/p2p/simulations/adapters/inproc.go +++ b/p2p/simulations/adapters/inproc.go @@ -224,9 +224,11 @@ func (self *SimNode) Start(snapshot []byte) error { services := []node.ServiceConstructor{} - for name, servicefunc := range self.serviceFuncs { - service := servicefunc(self.Id, snapshot) - + + // we need to control the order of the services + // for example, bzz needs to start before pss + for _, name := range self.config.Services { + service := self.serviceFuncs[name](self.Id, snapshot) services = append(services, func(ctx *node.ServiceContext) (node.Service, error) { self.running[name] = service return service, nil diff --git a/p2p/testing/protocoltester.go b/p2p/testing/protocoltester.go index e34fcf0265..306aa6baa7 100644 --- a/p2p/testing/protocoltester.go +++ b/p2p/testing/protocoltester.go @@ -18,7 +18,9 @@ type ProtocolTester struct { network *simulations.Network } -func NewProtocolTester(t *testing.T, id *adapters.NodeId, n int, run func(*p2p.Peer, p2p.MsgReadWriter) error) *ProtocolTester { +func NewProtocolTester(t *testing.T, id *adapters.NodeId, n int, moreservices adapters.Services, run func(*p2p.Peer, p2p.MsgReadWriter) error) *ProtocolTester { +//func NewProtocolTester(t *testing.T, id *adapters.NodeId, n int, run func(*p2p.Peer, p2p.MsgReadWriter) error) *ProtocolTester { + moreservicesstring := []string{} services := map[string]adapters.ServiceFunc{ "test": func(id *adapters.NodeId, _ []byte) node.Service { return &testNode{run} @@ -27,9 +29,13 @@ func NewProtocolTester(t *testing.T, id *adapters.NodeId, n int, run func(*p2p.P return newMockNode() }, } + for name, service := range moreservices { + services[name] = service + moreservicesstring = append(moreservicesstring, name) + } adapter := adapters.NewSimAdapter(services) net := simulations.NewNetwork(adapter, &simulations.NetworkConfig{}) - if _, err := net.NewNodeWithConfig(&adapters.NodeConfig{Id: id, Services: []string{"test"}}); err != nil { + if _, err := net.NewNodeWithConfig(&adapters.NodeConfig{Id: id, Services: append(moreservicesstring, "test")}); err != nil { panic(err.Error()) } if err := net.Start(id); err != nil { @@ -41,6 +47,7 @@ func NewProtocolTester(t *testing.T, id *adapters.NodeId, n int, run func(*p2p.P peerIDs := make([]*adapters.NodeId, n) for i := 0; i < n; i++ { peers[i] = adapters.RandomNodeConfig() + peers[i].Services = moreservicesstring peers[i].Services = append(peers[i].Services, "mock") peerIDs[i] = peers[i].Id } diff --git a/swarm/pss/common.go b/swarm/pss/common.go index 7b985a6fea..b9e91eb791 100644 --- a/swarm/pss/common.go +++ b/swarm/pss/common.go @@ -34,6 +34,8 @@ var pssPingProtocol = &protocols.Spec{ }, } +var pssPingTopic = NewTopic(pssPingProtocol.Name, int(pssPingProtocol.Version)) + func newTestPss(addr []byte) *Pss { if addr == nil { addr = network.RandomAddr().OAddr @@ -64,3 +66,24 @@ func newTestPss(addr []byte) *Pss { return ps } +func newPssPingMsg(ps *Pss, spec *protocols.Spec, topic PssTopic, senderaddr []byte) PssMsg { + data := pssPingMsg{ + Created: time.Now(), + } + code, found := spec.GetCode(&data) + if !found { + return PssMsg{} + } + + rlpbundle, err := newProtocolMsg(code, data) + if err != nil { + return PssMsg{} + } + + pssmsg := PssMsg{ + To: ps.Overlay.BaseAddr(), + Payload: NewPssEnvelope(senderaddr, topic, rlpbundle), + } + + return pssmsg +} diff --git a/swarm/pss/pss_test.go b/swarm/pss/pss_test.go index 02921c329f..59107cc85c 100644 --- a/swarm/pss/pss_test.go +++ b/swarm/pss/pss_test.go @@ -176,30 +176,29 @@ func TestPssRegisterHandler(t *testing.T) { func TestPssSimpleLinear(t *testing.T) { nodeconfig := adapters.RandomNodeConfig() addr := network.NewAddrFromNodeId(nodeconfig.Id) - pss := newTestPss(addr.OAddr) + ps := newTestPss(addr.OAddr) + pt := p2ptest.NewProtocolTester(t, nodeconfig.Id, 2, newServices(), ps.Protocols()[0].Run) - pt := p2ptest.NewProtocolTester(t, nodeconfig.Id, 2, pss.Protocols()[0].Run) - /* - return []p2ptest.Exchange{ - p2ptest.Exchange{ + msg := newPssPingMsg(ps, pssPingProtocol, pssPingTopic, []byte{1,2,3}) + + exchange := p2ptest.Exchange{ Expects: []p2ptest.Expect{ p2ptest.Expect{ Code: 0, - Msg: lhs, - Peer: id, + Msg: msg, + Peer: pt.Ids[1], }, }, Triggers: []p2ptest.Trigger{ p2ptest.Trigger{ Code: 0, - Msg: , - Peer: id, + Msg: msg, + Peer: pt.Ids[1], }, }, - }, - }*/ - - _ = pt + } + + pt.TestExchanges(exchange) } @@ -373,7 +372,7 @@ func newServices() adapters.Services { config.HiveParams.KeepAliveInterval = time.Second bzzs[id] = network.NewBzz(config) - + return bzzs[id] } @@ -390,6 +389,9 @@ func newServices() adapters.Services { return nil } pssp := NewPssParams() + for bzzs[id] == nil { + time.Sleep(time.Microsecond * 100) + } return NewPss(bzzs[id].Kademlia, dpa, pssp) } @@ -1301,35 +1303,6 @@ func makeCustomProtocol(name string, version int, ct *protocols.CodeMap, testpee return protocols.NewProtocol(name, uint(version), run, ct, nil, nil) } -func makeFakeMsg(ps *Pss, ct *protocols.CodeMap, topic PssTopic, senderaddr Addr, content string) PssMsg { - data := pssTestPayload{} - code, found := ct.GetCode(&data) - if !found { - return PssMsg{} - } - - data.Data = content - - rlpbundle, err := makeMsg(code, data) - if err != nil { - return PssMsg{} - } - - pssenv := pssEnvelope{ - SenderOAddr: senderaddr.Over(), - SenderUAddr: senderaddr.Under(), - Topic: topic, - TTL: DefaultTTL, - Payload: rlpbundle, - } - pssmsg := PssMsg{ - Payload: pssenv, - } - pssmsg.SetRecipient(ps.Overlay.BaseAddr()) - - return pssmsg -} - func makePssHandleForward(ps *Pss) func(msg interface{}) error { // for the simple check it passes on the message if it's not for us return func(msg interface{}) error { From aaeb8497a0e8539d1997ee743fcdee21170f3dbb Mon Sep 17 00:00:00 2001 From: nolash Date: Wed, 17 May 2017 17:50:33 +0200 Subject: [PATCH 21/24] WIP --- p2p/simulations/adapters/inproc.go | 3 +-- p2p/simulations/adapters/types.go | 3 ++- swarm/pss/pss_test.go | 14 +++----------- 3 files changed, 6 insertions(+), 14 deletions(-) diff --git a/p2p/simulations/adapters/inproc.go b/p2p/simulations/adapters/inproc.go index 2acf3d0e90..6f7c6a2538 100644 --- a/p2p/simulations/adapters/inproc.go +++ b/p2p/simulations/adapters/inproc.go @@ -225,8 +225,7 @@ func (self *SimNode) Start(snapshot []byte) error { services := []node.ServiceConstructor{} - // we need to control the order of the services - // for example, bzz needs to start before pss + // so we can control the order of the services if we need for _, name := range self.config.Services { service := self.serviceFuncs[name](self.Id, snapshot) services = append(services, func(ctx *node.ServiceContext) (node.Service, error) { diff --git a/p2p/simulations/adapters/types.go b/p2p/simulations/adapters/types.go index dacf3a2111..3165811a0f 100644 --- a/p2p/simulations/adapters/types.go +++ b/p2p/simulations/adapters/types.go @@ -205,10 +205,11 @@ func RandomNodeConfig() *NodeConfig { } // Services is a collection of services which can be run in a simulation +// it is mapped to strings representing TYPES of nodes type Services map[string]ServiceFunc // ServiceFunc returns a node.Service which can be used to boot devp2p nodes -type ServiceFunc func(id *NodeId, snapshot []byte) node.Service +type ServiceFunc func(id *NodeId, snapshot []byte) []node.Service // serviceFuncs is a map of registered services which are used to boot devp2p // nodes diff --git a/swarm/pss/pss_test.go b/swarm/pss/pss_test.go index 59107cc85c..e8467b715e 100644 --- a/swarm/pss/pss_test.go +++ b/swarm/pss/pss_test.go @@ -177,6 +177,7 @@ func TestPssSimpleLinear(t *testing.T) { nodeconfig := adapters.RandomNodeConfig() addr := network.NewAddrFromNodeId(nodeconfig.Id) ps := newTestPss(addr.OAddr) + ps.Register(pssPingTopic, pssPingHandler) pt := p2ptest.NewProtocolTester(t, nodeconfig.Id, 2, newServices(), ps.Protocols()[0].Run) msg := newPssPingMsg(ps, pssPingProtocol, pssPingTopic, []byte{1,2,3}) @@ -347,11 +348,7 @@ func triggerChecks(trigger chan *adapters.NodeId, net *simulations.Network, id * func newServices() adapters.Services { - bzzs := make(map[*adapters.NodeId]*network.Bzz) - - adaptersservices := make(map[string]adapters.ServiceFunc) - - adaptersservices["bzz"] = func(id *adapters.NodeId, snapshot []byte) node.Service { + return func(id *adapters.NodeId, snapshot []byte) []node.Service { // setup hive addr := network.NewAddrFromNodeId(id) @@ -371,12 +368,8 @@ func newServices() adapters.Services { config.HiveParams.KeepAliveInterval = time.Second - bzzs[id] = network.NewBzz(config) - - return bzzs[id] - } + network.NewBzz(config) - adaptersservices["pss"] = func(id *adapters.NodeId, snapshot []byte) node.Service { // pss setup cachedir, err := ioutil.TempDir("", "pss-cache") if err != nil { @@ -395,7 +388,6 @@ func newServices() adapters.Services { return NewPss(bzzs[id].Kademlia, dpa, pssp) } - return adaptersservices } /* From d219b0edb01b66a2f445121bd4ac9e3d16ba9c20 Mon Sep 17 00:00:00 2001 From: nolash Date: Thu, 18 May 2017 00:21:11 +0200 Subject: [PATCH 22/24] WIP --- p2p/simulations/adapters/docker.go | 10 +- p2p/simulations/adapters/exec.go | 37 +++-- p2p/simulations/adapters/inproc.go | 60 ++++--- p2p/simulations/adapters/types.go | 13 +- p2p/simulations/network.go | 6 +- p2p/testing/protocolsession.go | 4 +- p2p/testing/protocoltester.go | 23 ++- swarm/network/hive.go | 4 +- swarm/network/protocol.go | 4 +- swarm/network/simulations/overlay.go | 23 +-- swarm/pss/common.go | 16 ++ swarm/pss/pss.go | 31 ++-- swarm/pss/pss_test.go | 226 ++++++++++++++------------- swarm/pss/pssapi.go | 2 +- 14 files changed, 225 insertions(+), 234 deletions(-) diff --git a/p2p/simulations/adapters/docker.go b/p2p/simulations/adapters/docker.go index 7b58e7cfeb..f90c367bdb 100644 --- a/p2p/simulations/adapters/docker.go +++ b/p2p/simulations/adapters/docker.go @@ -9,6 +9,7 @@ import ( "os/exec" "path/filepath" "runtime" + "strings" "github.com/docker/docker/pkg/reexec" "github.com/ethereum/go-ethereum/node" @@ -42,10 +43,8 @@ func (d *DockerAdapter) Name() string { // NewNode returns a new DockerNode using the given config func (d *DockerAdapter) NewNode(config *NodeConfig) (Node, error) { - for _, name := range config.Services { - if _, exists := serviceFuncs[name]; !exists { - return nil, fmt.Errorf("unknown node service %q", name) - } + if _, exists := serviceFuncs[config.Service]; !exists { + return nil, fmt.Errorf("unknown node service %q", config.Service) } // generate the config @@ -62,7 +61,6 @@ func (d *DockerAdapter) NewNode(config *NodeConfig) (Node, error) { ExecNode: ExecNode{ ID: config.Id, Config: conf, - Services: config.Services, }, } node.newCmd = node.dockerCommand @@ -85,7 +83,7 @@ func (n *DockerNode) dockerCommand() *exec.Cmd { "sh", "-c", fmt.Sprintf( `exec docker run --interactive --env _P2P_NODE_CONFIG="${_P2P_NODE_CONFIG}" --env _P2P_NODE_KEY="${_P2P_NODE_KEY}" %s p2p-node %s %s`, - dockerImage, n.Services[0], n.ID.String(), + dockerImage, strings.Join(n.Services, " "), n.ID.String(), ), ) } diff --git a/p2p/simulations/adapters/exec.go b/p2p/simulations/adapters/exec.go index 376c03c6db..a535e80698 100644 --- a/p2p/simulations/adapters/exec.go +++ b/p2p/simulations/adapters/exec.go @@ -46,10 +46,8 @@ func (e *ExecAdapter) Name() string { // NewNode returns a new ExecNode using the given config func (e *ExecAdapter) NewNode(config *NodeConfig) (Node, error) { - for _, name := range config.Services { - if _, exists := serviceFuncs[name]; !exists { - return nil, fmt.Errorf("unknown node service %q", name) - } + if _, exists := serviceFuncs[config.Service]; !exists { + return nil, fmt.Errorf("unknown node service %q", config.Service) } // create the node directory using the first 12 characters of the ID @@ -77,7 +75,6 @@ func (e *ExecAdapter) NewNode(config *NodeConfig) (Node, error) { ID: config.Id, Dir: dir, Config: conf, - Services: config.Services, } node.newCmd = node.execCommand return node, nil @@ -91,11 +88,11 @@ func (e *ExecAdapter) NewNode(config *NodeConfig) (Node, error) { // (so for example we can run the node in a remote Docker container and // still communicate with it). type ExecNode struct { - ID *NodeId - Dir string - Config *execNodeConfig - Cmd *exec.Cmd - Info *p2p.NodeInfo + ID *NodeId + Dir string + Config *execNodeConfig + Cmd *exec.Cmd + Info *p2p.NodeInfo Services []string client *rpc.Client @@ -168,7 +165,6 @@ func (n *ExecNode) Start(snapshot []byte) (err error) { return nil } - func (n *ExecNode) GetService(name string) node.Service { return nil } @@ -287,7 +283,7 @@ func execP2PNode() { if !exists { log.Crit(fmt.Sprintf("unknown node service %q", serviceName)) } - service := serviceFunc(id, conf.Snapshot) + services := serviceFunc(id, conf.Snapshot) // use explicit IP address in ListenAddr so that Enode URL is usable if strings.HasPrefix(conf.Stack.P2P.ListenAddr, ":") { @@ -304,7 +300,7 @@ func execP2PNode() { } // start the devp2p stack - stack, err := startP2PNode(&conf.Stack, service) + stack, err := startP2PNode(&conf.Stack, services) if err != nil { log.Crit("error starting p2p node", "err", err) } @@ -330,17 +326,20 @@ func execP2PNode() { stack.Wait() } -func startP2PNode(conf *node.Config, service node.Service) (*node.Node, error) { +func startP2PNode(conf *node.Config, services []node.Service) (*node.Node, error) { stack, err := node.New(conf) if err != nil { return nil, err } - constructor := func(ctx *node.ServiceContext) (node.Service, error) { - return &snapshotService{service}, nil - } - if err := stack.Register(constructor); err != nil { - return nil, err + for _, svc := range services { + constructor := func(ctx *node.ServiceContext) (node.Service, error) { + return &snapshotService{svc}, nil + } + if err := stack.Register(constructor); err != nil { + return nil, err + } } + if err := stack.Start(); err != nil { return nil, err } diff --git a/p2p/simulations/adapters/inproc.go b/p2p/simulations/adapters/inproc.go index 6f7c6a2538..37438ce276 100644 --- a/p2p/simulations/adapters/inproc.go +++ b/p2p/simulations/adapters/inproc.go @@ -21,6 +21,7 @@ import ( "fmt" "math" "net" + "reflect" "sync" "github.com/ethereum/go-ethereum/event" @@ -36,7 +37,6 @@ import ( type SimAdapter struct { mtx sync.RWMutex nodes map[discover.NodeID]*SimNode - services map[string]ServiceFunc } // NewSimAdapter creates a SimAdapter which is capable of running in-memory @@ -45,7 +45,6 @@ type SimAdapter struct { func NewSimAdapter(services map[string]ServiceFunc) *SimAdapter { return &SimAdapter{ nodes: make(map[discover.NodeID]*SimNode), - services: services, } } @@ -100,29 +99,19 @@ func (s *SimAdapter) NewNode(config *NodeConfig) (Node, error) { if err != nil { return nil, err } - - servicefuncs := make(map[string]ServiceFunc) - - for name, servicefunc := range s.services { - service := servicefunc(id, nil) - /*if err := n.Register(func(ctx *node.ServiceContext) (node.Service, error) { - return service, err - }); err != nil { - return nil, err - }*/ + + for _, service := range serviceFuncs[config.Service](id, nil) { for _, proto := range service.Protocols() { nodeprotos = append(nodeprotos, proto) } - servicefuncs[name] = servicefunc } simnode := &SimNode{ - //node: n, Id: id, - serviceFuncs: servicefuncs, + serviceFunc: serviceFuncs[config.Service], adapter: s, config: config, - running: make(map[string]node.Service), + running: []node.Service{}, } s.nodes[id.NodeID] = simnode return simnode, nil @@ -161,11 +150,11 @@ type SimNode struct { Id *NodeId config *NodeConfig adapter *SimAdapter - serviceFuncs map[string]ServiceFunc + serviceFunc ServiceFunc node *node.Node - running map[string]node.Service client *rpc.Client rpcMux *rpcMux + running []node.Service } // Addr returns the node's discovery address @@ -224,14 +213,16 @@ func (self *SimNode) Start(snapshot []byte) error { services := []node.ServiceConstructor{} + sf := self.serviceFunc(self.Id, snapshot) - // so we can control the order of the services if we need - for _, name := range self.config.Services { - service := self.serviceFuncs[name](self.Id, snapshot) - services = append(services, func(ctx *node.ServiceContext) (node.Service, error) { - self.running[name] = service + for i, _ := range sf { + service := sf[i] + sc := func(ctx *node.ServiceContext) (node.Service, error) { return service, nil - }) + } + log.Debug(fmt.Sprintf("servicefunc yield: %v %p %p", reflect.TypeOf(sf[i]), sf[i], sc)) + services = append(services, sc) + self.running = append(self.running, sf[i]) } node, err := node.New(&node.Config{ @@ -249,7 +240,7 @@ func (self *SimNode) Start(snapshot []byte) error { } for _, service := range services { - log.Debug("registering service", "service", service) + log.Debug(fmt.Sprintf("service %v", service)) if err := node.Register(service); err != nil { return err } @@ -290,6 +281,19 @@ func (self *SimNode) Stop() error { return nil } +// Service returns the underlying running node.Service matching the supplied servuce type +func (self *SimNode) Service(servicetype interface{}) node.Service { + self.lock.Lock() + defer self.lock.Unlock() + typ := reflect.TypeOf(servicetype) + for _, service := range self.running { + if reflect.TypeOf(service) == typ { + return service + } + } + return nil +} + func (self *SimNode) Server() *p2p.Server { self.lock.Lock() defer self.lock.Unlock() @@ -299,12 +303,6 @@ func (self *SimNode) Server() *p2p.Server { return self.node.Server() } -// Service returns a underlying node.Service of the speficied type -func (self *SimNode) GetService(servicename string) node.Service { - log.Warn("retrieving service", "name", servicename) - return self.running[servicename] -} - func (self *SimNode) SubscribeEvents(ch chan *p2p.PeerEvent) event.Subscription { srv := self.Server() if srv == nil { diff --git a/p2p/simulations/adapters/types.go b/p2p/simulations/adapters/types.go index 3165811a0f..c418cb7e32 100644 --- a/p2p/simulations/adapters/types.go +++ b/p2p/simulations/adapters/types.go @@ -62,9 +62,6 @@ type Node interface { // Snapshot creates a snapshot of the running service Snapshot() ([]byte, error) - - // Gets a service by name - GetService(string) node.Service } // NodeAdapter is an object which creates Nodes to be used in a simulation @@ -130,11 +127,11 @@ type NodeConfig struct { // Name is a human friendly name for the node like "node01" Name string - // Services is the name of the services which should be run when starting + // Service is the name of the services which should be run when starting // the node (for SimNodes it should be the names of services contained // in SimAdapter.services, for other nodes it should be services // registered by calling the RegisterService function) - Services []string + Service string } // nodeConfigJSON is used to encode and decode NodeConfig as JSON by converting @@ -143,13 +140,13 @@ type nodeConfigJSON struct { Id string `json:"id"` PrivateKey string `json:"private_key"` Name string `json:"name"` - Services []string `json:"services"` + Service string `json:"service"` } func (n *NodeConfig) MarshalJSON() ([]byte, error) { confJSON := nodeConfigJSON{ Name: n.Name, - Services: n.Services, + Service: n.Service, } if n.Id != nil { confJSON.Id = n.Id.String() @@ -183,7 +180,7 @@ func (n *NodeConfig) UnmarshalJSON(data []byte) error { } n.Name = confJSON.Name - n.Services = confJSON.Services + n.Service = confJSON.Service return nil } diff --git a/p2p/simulations/network.go b/p2p/simulations/network.go index 84d06e089e..77cd4b9cbd 100644 --- a/p2p/simulations/network.go +++ b/p2p/simulations/network.go @@ -190,7 +190,7 @@ func (self *Msg) String() string { // NewNode adds a new node to the network with a random ID func (self *Network) NewNode() (*Node, error) { conf := adapters.RandomNodeConfig() - conf.Services = append(conf.Services, self.DefaultService) + conf.Service = self.DefaultService return self.NewNodeWithConfig(conf) } @@ -203,8 +203,8 @@ func (self *Network) NewNodeWithConfig(conf *adapters.NodeConfig) (*Node, error) if conf.Name == "" { conf.Name = fmt.Sprintf("node%02d", len(self.Nodes)+1) } - if len(conf.Services) == 0 { - conf.Services = append(conf.Services, self.DefaultService) + if conf.Service == "" { + conf.Service = self.DefaultService } _, found := self.nodeMap[id.NodeID] diff --git a/p2p/testing/protocolsession.go b/p2p/testing/protocolsession.go index 98c0bf7f2a..eda8441136 100644 --- a/p2p/testing/protocolsession.go +++ b/p2p/testing/protocolsession.go @@ -57,7 +57,7 @@ func (self *ProtocolSession) trigger(trig Trigger) error { if !ok { return fmt.Errorf("trigger: peer %v does not exist (1- %v)", trig.Peer, len(self.Ids)) } - mockNode, ok := simNode.GetService("mock").(*mockNode) + mockNode, ok := simNode.Service(&mockNode{}).(*mockNode) if !ok { return fmt.Errorf("trigger: peer %v is not a mock", trig.Peer) } @@ -92,7 +92,7 @@ func (self *ProtocolSession) expect(exp Expect) error { if !ok { return fmt.Errorf("trigger: peer %v does not exist (1- %v)", exp.Peer, len(self.Ids)) } - mockNode, ok := simNode.GetService("mock").(*mockNode) + mockNode, ok := simNode.Service(&mockNode{}).(*mockNode) if !ok { return fmt.Errorf("trigger: peer %v is not a mock", exp.Peer) } diff --git a/p2p/testing/protocoltester.go b/p2p/testing/protocoltester.go index 306aa6baa7..a328cb06d7 100644 --- a/p2p/testing/protocoltester.go +++ b/p2p/testing/protocoltester.go @@ -18,24 +18,20 @@ type ProtocolTester struct { network *simulations.Network } -func NewProtocolTester(t *testing.T, id *adapters.NodeId, n int, moreservices adapters.Services, run func(*p2p.Peer, p2p.MsgReadWriter) error) *ProtocolTester { +func NewProtocolTester(t *testing.T, id *adapters.NodeId, n int, run func(*p2p.Peer, p2p.MsgReadWriter) error) *ProtocolTester { //func NewProtocolTester(t *testing.T, id *adapters.NodeId, n int, run func(*p2p.Peer, p2p.MsgReadWriter) error) *ProtocolTester { - moreservicesstring := []string{} - services := map[string]adapters.ServiceFunc{ - "test": func(id *adapters.NodeId, _ []byte) node.Service { - return &testNode{run} + services := adapters.Services { + "test": func(id *adapters.NodeId, _ []byte) []node.Service { + return []node.Service{&testNode{run}} }, - "mock": func(id *adapters.NodeId, _ []byte) node.Service { - return newMockNode() + "mock": func(id *adapters.NodeId, _ []byte) []node.Service { + return []node.Service{newMockNode()} }, } - for name, service := range moreservices { - services[name] = service - moreservicesstring = append(moreservicesstring, name) - } + adapters.RegisterServices(services) adapter := adapters.NewSimAdapter(services) net := simulations.NewNetwork(adapter, &simulations.NetworkConfig{}) - if _, err := net.NewNodeWithConfig(&adapters.NodeConfig{Id: id, Services: append(moreservicesstring, "test")}); err != nil { + if _, err := net.NewNodeWithConfig(&adapters.NodeConfig{Id: id, Service: "test"}); err != nil { panic(err.Error()) } if err := net.Start(id); err != nil { @@ -47,8 +43,7 @@ func NewProtocolTester(t *testing.T, id *adapters.NodeId, n int, moreservices ad peerIDs := make([]*adapters.NodeId, n) for i := 0; i < n; i++ { peers[i] = adapters.RandomNodeConfig() - peers[i].Services = moreservicesstring - peers[i].Services = append(peers[i].Services, "mock") + peers[i].Service = "mock" peerIDs[i] = peers[i].Id } events := make(chan *p2p.PeerEvent, 1000) diff --git a/swarm/network/hive.go b/swarm/network/hive.go index 9fb20d2815..f9a437868f 100644 --- a/swarm/network/hive.go +++ b/swarm/network/hive.go @@ -80,7 +80,7 @@ func NewHiveParams() *HiveParams { type Hive struct { *HiveParams // settings Overlay // the overlay topology driver - store Store + store StateStore // bookkeeping lock sync.Mutex @@ -93,7 +93,7 @@ type Hive struct { // Hive constructor embeds both arguments // HiveParams: config parameters // Overlay: Topology Driver Interface -func NewHive(params *HiveParams, overlay Overlay, store Store) *Hive { +func NewHive(params *HiveParams, overlay Overlay, store StateStore) *Hive { return &Hive{ HiveParams: params, Overlay: overlay, diff --git a/swarm/network/protocol.go b/swarm/network/protocol.go index cb8b4eca2f..3901eadc2c 100644 --- a/swarm/network/protocol.go +++ b/swarm/network/protocol.go @@ -85,7 +85,7 @@ type Conn interface { } // TODO: implement store for exec nodes -type Store interface { +type StateStore interface { Load(string) ([]byte, error) Save(string, []byte) error } @@ -106,7 +106,7 @@ type Bzz struct { } // NewBzz is the swarm protocol constructor -func NewBzz(config *BzzConfig, kad Overlay, store Store) *Bzz { +func NewBzz(config *BzzConfig, kad Overlay, store StateStore) *Bzz { return &Bzz{ Hive: NewHive(config.HiveParams, kad, store), localAddr: &bzzAddr{config.OverlayAddr, config.UnderlayAddr}, diff --git a/swarm/network/simulations/overlay.go b/swarm/network/simulations/overlay.go index 70bd53e3ec..9c7ce159e6 100644 --- a/swarm/network/simulations/overlay.go +++ b/swarm/network/simulations/overlay.go @@ -22,33 +22,14 @@ import ( "github.com/ethereum/go-ethereum/swarm/network" ) -type simStore struct { - m map[string][]byte -} - -func (self *simStore) Load(s string) ([]byte, error) { - return self.m[s], nil -} - -func (self *simStore) Save(s string, data []byte) error { - self.m[s] = data - return nil -} - -func NewSimStore() *simStore { - return &simStore{ - make(map[string][]byte), - } -} - type Simulation struct { mtx sync.Mutex - stores map[discover.NodeID]*simStore + stores map[discover.NodeID]*adapters.stateStore } func NewSimulation() *Simulation { return &Simulation{ - stores: make(map[discover.NodeID]*simStore), + stores: make(map[discover.NodeID]*adapters.stateStore), } } diff --git a/swarm/pss/common.go b/swarm/pss/common.go index b9e91eb791..ea0a9bf90a 100644 --- a/swarm/pss/common.go +++ b/swarm/pss/common.go @@ -1,11 +1,13 @@ package pss import ( + "fmt" "io/ioutil" "os" "time" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/protocols" "github.com/ethereum/go-ethereum/swarm/network" "github.com/ethereum/go-ethereum/swarm/storage" @@ -87,3 +89,17 @@ func newPssPingMsg(ps *Pss, spec *protocols.Spec, topic PssTopic, senderaddr []b return pssmsg } + +func newPssPingProtocol(handler func (interface{}) error) *p2p.Protocol { + return &p2p.Protocol{ + Name: pssPingProtocol.Name, + Version: pssPingProtocol.Version, + Length: uint64(pssPingProtocol.MaxMsgSize), + Run: func(p *p2p.Peer, rw p2p.MsgReadWriter) error { + pp := protocols.NewPeer(p, rw, pssPingProtocol) + log.Trace(fmt.Sprintf("running pss vprotocol on peer %v", p)) + err := pp.Run(handler) + return err + }, + } +} diff --git a/swarm/pss/pss.go b/swarm/pss/pss.go index f3369463bd..26ac098cae 100644 --- a/swarm/pss/pss.go +++ b/swarm/pss/pss.go @@ -191,16 +191,16 @@ func (self *Pss) Protocols() []p2p.Protocol { Name: pssTransportProtocol.Name, Version: pssTransportProtocol.Version, Length: pssTransportProtocol.Length(), - Run: func(p *p2p.Peer, rw p2p.MsgReadWriter) error { - pp := protocols.NewPeer(p, rw, pssTransportProtocol) - err := pp.Run(self.handlePssMsg) - log.Warn("pss protocol peer returned", "peer", p, "err", err) - return nil - }, + Run: self.Run, }, } } +func (self *Pss) Run(p *p2p.Peer, rw p2p.MsgReadWriter) error { + pp := protocols.NewPeer(p, rw, pssTransportProtocol) + return pp.Run(self.handlePssMsg) +} + func (self *Pss) APIs() []rpc.API { return []rpc.API{ rpc.API { @@ -218,24 +218,24 @@ func (self *Pss) APIs() []rpc.API { // a topic allows for multiple handlers // returns a deregister function which needs to be called to deregister the handler // (similar to event.Subscription.Unsubscribe()) -func (self *Pss) Register(topic PssTopic, handler pssHandler) func() { +func (self *Pss) Register(topic *PssTopic, handler pssHandler) func() { self.lock.Lock() defer self.lock.Unlock() - handlers := self.handlers[topic] + handlers := self.handlers[*topic] if handlers == nil { handlers = make(map[*pssHandler]bool) - self.handlers[topic] = handlers + self.handlers[*topic] = handlers } handlers[&handler] = true return func() { self.deregister(topic, &handler) } } -func (self *Pss) deregister(topic PssTopic, h *pssHandler) { +func (self *Pss) deregister(topic *PssTopic, h *pssHandler) { self.lock.Lock() defer self.lock.Unlock() - handlers := self.handlers[topic] + handlers := self.handlers[*topic] if len(handlers) == 1 { - delete(self.handlers, topic) + delete(self.handlers, *topic) return } delete(handlers, h) @@ -497,14 +497,17 @@ type PssProtocol struct { } // Constructor -func NewPssProtocol(pss *Pss, topic *PssTopic, spec *protocols.Spec, targetprotocol *p2p.Protocol) *PssProtocol { +//func RegisterPssProtocol(pss *Pss, topic *PssTopic, spec *protocols.Spec, targetprotocol *p2p.Protocol) *PssProtocol { +func RegisterPssProtocol(pss *Pss, topic *PssTopic, spec *protocols.Spec, targetprotocol *p2p.Protocol) error { pp := &PssProtocol{ Pss: pss, proto: targetprotocol, topic: topic, spec: spec, } - return pp + pss.Register(topic, pp.handle) + //return pp + return nil } func (self *PssProtocol) handle(msg []byte, p *p2p.Peer, senderAddr []byte) error { diff --git a/swarm/pss/pss_test.go b/swarm/pss/pss_test.go index e8467b715e..1a02a70067 100644 --- a/swarm/pss/pss_test.go +++ b/swarm/pss/pss_test.go @@ -9,7 +9,7 @@ import ( "os" "testing" "time" - + "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/p2p" @@ -21,12 +21,10 @@ import ( ) const ( - pssServiceName = "pss" - bzzServiceName = "bzz" + pssServiceName = "pss" + bzzServiceName = "bzz" ) -var topic PssTopic = NewTopic(pssPingProtocol.Name, int(pssPingProtocol.Version)) - var services = newServices() func init() { @@ -49,19 +47,19 @@ func TestPssCache(t *testing.T) { fwdaddr := network.RandomAddr() msg := &PssMsg{ Payload: &PssEnvelope{ - TTL: 0, - From: oaddr, - Topic: topic, + TTL: 0, + From: oaddr, + Topic: pssPingTopic, Payload: data, }, To: to, } - + msgtwo := &PssMsg{ Payload: &PssEnvelope{ - TTL: 0, - From: oaddr, - Topic: topic, + TTL: 0, + From: oaddr, + Topic: pssPingTopic, Payload: datatwo, }, To: to, @@ -75,7 +73,7 @@ func TestPssCache(t *testing.T) { if err != nil { t.Fatalf("could not store cache msgtwo: %v", err) } - + if !bytes.Equal(digest[:], proofbytes) { t.Fatalf("digest - got: %x, expected: %x", digest, proofbytes) } @@ -144,7 +142,7 @@ func TestPssRegisterHandler(t *testing.T) { } return nil } - deregister := ps.Register(topic, checkMsg) + deregister := ps.Register(&topic, checkMsg) pssmsg := &PssMsg{Payload: NewPssEnvelope(from.OAddr, topic, payload)} err = ps.Process(pssmsg) if err != nil { @@ -156,7 +154,7 @@ func TestPssRegisterHandler(t *testing.T) { if err == nil || err.Error() == expErr { t.Fatalf("unhandled topic expected '%v', got '%v'", expErr, err) } - deregister2 := ps.Register(topic, func(msg []byte, p *p2p.Peer, sender []byte) error { i++; return nil }) + deregister2 := ps.Register(&topic, func(msg []byte, p *p2p.Peer, sender []byte) error { i++; return nil }) err = ps.Process(pssmsg) if err != nil { t.Fatal(err) @@ -176,33 +174,42 @@ func TestPssRegisterHandler(t *testing.T) { func TestPssSimpleLinear(t *testing.T) { nodeconfig := adapters.RandomNodeConfig() addr := network.NewAddrFromNodeId(nodeconfig.Id) + _ = p2ptest.NewTestPeerPool() ps := newTestPss(addr.OAddr) - ps.Register(pssPingTopic, pssPingHandler) - pt := p2ptest.NewProtocolTester(t, nodeconfig.Id, 2, newServices(), ps.Protocols()[0].Run) - - msg := newPssPingMsg(ps, pssPingProtocol, pssPingTopic, []byte{1,2,3}) - + + ping := &pssPing{ + quitC: make(chan struct{}), + } + + err := RegisterPssProtocol(ps, &pssPingTopic, pssPingProtocol, newPssPingProtocol(ping.pssPingHandler)) + + if err != nil { + t.Fatalf("Failed to register virtual protocol in pss: %v", err) + } + pt := p2ptest.NewProtocolTester(t, nodeconfig.Id, 2, ps.Run) + + msg := newPssPingMsg(ps, pssPingProtocol, pssPingTopic, []byte{1, 2, 3}) + exchange := p2ptest.Exchange{ - Expects: []p2ptest.Expect{ - p2ptest.Expect{ - Code: 0, - Msg: msg, - Peer: pt.Ids[1], - }, + Expects: []p2ptest.Expect{ + p2ptest.Expect{ + Code: 0, + Msg: msg, + Peer: pt.Ids[0], }, - Triggers: []p2ptest.Trigger{ - p2ptest.Trigger{ - Code: 0, - Msg: msg, - Peer: pt.Ids[1], - }, + }, + Triggers: []p2ptest.Trigger{ + p2ptest.Trigger{ + Code: 0, + Msg: msg, + Peer: pt.Ids[1], }, - } - + }, + } + pt.TestExchanges(exchange) } - func TestPssFullRandom10_5_5(t *testing.T) { adapter := adapters.NewSimAdapter(services) testPssFullRandom(t, adapter, 10, 5, 5) @@ -210,19 +217,20 @@ func TestPssFullRandom10_5_5(t *testing.T) { func testPssFullRandom(t *testing.T, adapter adapters.NodeAdapter, nodecount int, fullnodecount int, msgcount int) { var lastid *adapters.NodeId = nil + nodeCount := 5 net := simulations.NewNetwork(adapter, &simulations.NetworkConfig{ Id: "0", - DefaultService: bzzServiceName, + DefaultService: "psstest", }) defer net.Shutdown() - + trigger := make(chan *adapters.NodeId) ids := make([]*adapters.NodeId, nodeCount) - + for i := 0; i < nodeCount; i++ { nodeconfig := adapters.RandomNodeConfig() - nodeconfig.Services = []string{"bzz", "pss"} + nodeconfig.Service = "psstest" node, err := net.NewNodeWithConfig(nodeconfig) if err != nil { t.Fatalf("error starting node: %s", err) @@ -231,13 +239,13 @@ func testPssFullRandom(t *testing.T, adapter adapters.NodeAdapter, nodecount int if err := net.Start(node.ID()); err != nil { t.Fatalf("error starting node %s: %s", node.ID().Label(), err) } - + if err := triggerChecks(trigger, net, node.ID()); err != nil { t.Fatal("error triggering checks for node %s: %s", node.ID().Label(), err) } ids[i] = node.ID() } - + // run a simulation which connects the 10 nodes in a ring and waits // for full peer discovery action := func(ctx context.Context) error { @@ -269,21 +277,21 @@ func testPssFullRandom(t *testing.T, adapter adapters.NodeAdapter, nodecount int if err != nil { return false, fmt.Errorf("error getting node client: %s", err) } - + log.Debug("in check", "node", id) - + if lastid != nil { //msg := pssPingMsg{Created: time.Now(),} - client.CallContext(context.Background(), nil, "pss_sendRaw", topic, PssAPIMsg{ + client.CallContext(context.Background(), nil, "pss_sendRaw", pssPingTopic, PssAPIMsg{ Addr: lastid.Bytes(), - Msg: []byte{1,2,3}, + Msg: []byte{1, 2, 3}, }) } lastid = id - + return true, nil } - + timeout := 5 * time.Second ctx, cancel := context.WithTimeout(context.Background(), timeout) defer cancel() @@ -302,7 +310,7 @@ func testPssFullRandom(t *testing.T, adapter adapters.NodeAdapter, nodecount int t.Log("Simulation Passed:") t.Logf("Duration: %s", result.FinishedAt.Sub(result.StartedAt)) - + time.Sleep(time.Second * 2) } @@ -313,81 +321,77 @@ func triggerChecks(trigger chan *adapters.NodeId, net *simulations.Network, id * if node == nil { return fmt.Errorf("unknown node: %s", id) } - go func(){ + go func() { time.Sleep(time.Second) trigger <- id }() /* - client, err := node.Client() - if err != nil { - return err - } - events := make(chan PssAPIMsg) - sub, err := client.Subscribe(context.Background(), "pss", events, "newMsg", topic) - if err != nil { - return fmt.Errorf("error getting peer events for node %v: %s", id, err) - } - go func() { - defer sub.Unsubscribe() - for { - select { - case msg := <-events: - log.Warn("pss rpc got msg", "msg", msg) - trigger <- id - case err := <-sub.Err(): - if err != nil { - log.Error(fmt.Sprintf("error getting peer events for node %v", id), "err", err) - } - return - } + client, err := node.Client() + if err != nil { + return err } - }() + events := make(chan PssAPIMsg) + sub, err := client.Subscribe(context.Background(), "pss", events, "newMsg", topic) + if err != nil { + return fmt.Errorf("error getting peer events for node %v: %s", id, err) + } + go func() { + defer sub.Unsubscribe() + for { + select { + case msg := <-events: + log.Warn("pss rpc got msg", "msg", msg) + trigger <- id + case err := <-sub.Err(): + if err != nil { + log.Error(fmt.Sprintf("error getting peer events for node %v", id), "err", err) + } + return + } + } + }() */ return nil } func newServices() adapters.Services { - - return func(id *adapters.NodeId, snapshot []byte) []node.Service { - // setup hive - addr := network.NewAddrFromNodeId(id) - config := &network.BzzConfig{ - OverlayAddr: addr.Over(), - UnderlayAddr: addr.Under(), - KadParams: network.NewKadParams(), - HiveParams: network.NewHiveParams(), - } + return adapters.Services{ + "psstest": func(id *adapters.NodeId, snapshot []byte) []node.Service { + addr := network.NewAddrFromNodeId(id) - config.KadParams.MinProxBinSize = 2 - config.KadParams.MaxBinSize = 3 - config.KadParams.MinBinSize = 1 - config.KadParams.MaxRetries = 1000 - config.KadParams.RetryExponent = 2 - config.KadParams.RetryInterval = 1000000 + kadparams := network.NewKadParams() + kadparams.MinProxBinSize = 2 + kadparams.MaxBinSize = 3 + kadparams.MinBinSize = 1 + kadparams.MaxRetries = 1000 + kadparams.RetryExponent = 2 + kadparams.RetryInterval = 1000000 + kademlia := network.NewKademlia(addr.OAddr, kadparams) - config.HiveParams.KeepAliveInterval = time.Second + config := &network.BzzConfig{ + OverlayAddr: addr.Over(), + UnderlayAddr: addr.Under(), + HiveParams: network.NewHiveParams(), + } - network.NewBzz(config) - - // pss setup - cachedir, err := ioutil.TempDir("", "pss-cache") - if err != nil { - log.Error("create pss cache tmpdir failed", "error", err) - return nil - } - dpa, err := storage.NewLocalDPA(cachedir) - if err != nil { - log.Error("local dpa creation failed", "error", err) - return nil - } - pssp := NewPssParams() - for bzzs[id] == nil { - time.Sleep(time.Microsecond * 100) - } - return NewPss(bzzs[id].Kademlia, dpa, pssp) + config.HiveParams.KeepAliveInterval = time.Second + + cachedir, err := ioutil.TempDir("", "pss-cache") + if err != nil { + log.Error("create pss cache tmpdir failed", "error", err) + return nil + } + dpa, err := storage.NewLocalDPA(cachedir) + if err != nil { + log.Error("local dpa creation failed", "error", err) + return nil + } + pssp := NewPssParams() + + return []node.Service{network.NewBzz(config, kademlia, adapters.NewSimStateStore()), NewPss(kademlia, dpa, pssp)} + }, } - } /* @@ -1209,14 +1213,14 @@ func newPssSimulationTester(t *testing.T, numnodes int, numfullnodes int, trigge } func makePss(addr []byte) *Pss { - + // set up storage cachedir, err := ioutil.TempDir("", "pss-cache") if err != nil { log.Error("create pss cache tmpdir failed", "error", err) os.Exit(1) } - + dpa, err := storage.NewLocalDPA(cachedir) if err != nil { log.Error("local dpa creation failed", "error", err) @@ -1268,7 +1272,7 @@ func makePss(addr []byte) *Pss { } } // dpa.Chunker = storage.NewPyramidChunker(storage.NewChunkerParams()) - + kp := network.NewKadParams() kp.MinProxBinSize = 3 diff --git a/swarm/pss/pssapi.go b/swarm/pss/pssapi.go index 2943d8d9ea..d0d9abd25a 100644 --- a/swarm/pss/pssapi.go +++ b/swarm/pss/pssapi.go @@ -44,7 +44,7 @@ func (pssapi *PssAPI) NewMsg(ctx context.Context, topic PssTopic) (*rpc.Subscrip } return nil } - deregf := pssapi.Pss.Register(topic, handler) + deregf := pssapi.Pss.Register(&topic, handler) go func() { defer deregf() From a924c8572e521e849b96eda373767b8d97bbf2e4 Mon Sep 17 00:00:00 2001 From: nolash Date: Thu, 18 May 2017 06:11:59 +0200 Subject: [PATCH 23/24] swarm/pss, swarm/network, p2p/simulations: pss prototest fix --- p2p/simulations/adapters/state.go | 20 ++++++++++++++++++++ swarm/network/protocol.go | 8 ++++---- swarm/pss/common.go | 24 ++++++++++++++++++++++-- swarm/pss/pss.go | 25 +++++++++++++++---------- swarm/pss/pss_test.go | 31 ++++++++++++++++++++++++------- 5 files changed, 85 insertions(+), 23 deletions(-) create mode 100644 p2p/simulations/adapters/state.go diff --git a/p2p/simulations/adapters/state.go b/p2p/simulations/adapters/state.go new file mode 100644 index 0000000000..2d7882c86b --- /dev/null +++ b/p2p/simulations/adapters/state.go @@ -0,0 +1,20 @@ +package adapters + +type SimStateStore struct { + m map[string][]byte +} + +func (self *SimStateStore) Load(s string) ([]byte, error) { + return self.m[s], nil +} + +func (self *SimStateStore) Save(s string, data []byte) error { + self.m[s] = data + return nil +} + +func NewSimStateStore() *SimStateStore { + return &SimStateStore{ + make(map[string][]byte), + } +} diff --git a/swarm/network/protocol.go b/swarm/network/protocol.go index 3901eadc2c..7753a69b1c 100644 --- a/swarm/network/protocol.go +++ b/swarm/network/protocol.go @@ -347,15 +347,15 @@ func NewNodeIdFromAddr(addr Addr) *adapters.NodeId { // NewAddrFromNodeId constucts a bzzAddr from an adapters.NodeId // the overlay address is derived as the hash of the nodeId -func NewAddrFromNodeId(n *adapters.NodeId) *bzzAddr { +func NewAddrFromNodeId(n *adapters.NodeId) Addr { id := n.NodeID return &bzzAddr{ - OAddr: ToOverlayAddr(n), + OAddr: ToOverlayAddr(n.Bytes()), UAddr: []byte(discover.NewNode(id, net.IP{127, 0, 0, 1}, 30303, 30303).String()), } } // ToOverlayAddr creates an overlayaddress from NodeID -func ToOverlayAddr(id *adapters.NodeId) []byte { - return crypto.Keccak256(id.Bytes()) +func ToOverlayAddr(id []byte) []byte { + return crypto.Keccak256(id) } diff --git a/swarm/pss/common.go b/swarm/pss/common.go index ea0a9bf90a..c3f03698ec 100644 --- a/swarm/pss/common.go +++ b/swarm/pss/common.go @@ -68,7 +68,7 @@ func newTestPss(addr []byte) *Pss { return ps } -func newPssPingMsg(ps *Pss, spec *protocols.Spec, topic PssTopic, senderaddr []byte) PssMsg { +func newPssPingMsg(ps *Pss, to []byte, spec *protocols.Spec, topic PssTopic, senderaddr []byte) PssMsg { data := pssPingMsg{ Created: time.Now(), } @@ -83,7 +83,7 @@ func newPssPingMsg(ps *Pss, spec *protocols.Spec, topic PssTopic, senderaddr []b } pssmsg := PssMsg{ - To: ps.Overlay.BaseAddr(), + To: to, Payload: NewPssEnvelope(senderaddr, topic, rlpbundle), } @@ -103,3 +103,23 @@ func newPssPingProtocol(handler func (interface{}) error) *p2p.Protocol { }, } } + +type testPssPeer struct { + *protocols.Peer + addr []byte +} + +func (self *testPssPeer) Address() []byte { + return self.addr +} + +func (self *testPssPeer) Off() network.OverlayAddr { + return self +} + +func (self *testPssPeer) Drop(err error) { +} + +func (self *testPssPeer) Update(o network.OverlayAddr) network.OverlayAddr { + return self +} diff --git a/swarm/pss/pss.go b/swarm/pss/pss.go index 26ac098cae..36873fcee0 100644 --- a/swarm/pss/pss.go +++ b/swarm/pss/pss.go @@ -37,6 +37,11 @@ var ( errorForwardToSelf = errors.New("forward to self") ) +type senderPeer interface { + Address() []byte + Send(interface{}) error +} + // Defines params for Pss type PssParams struct { Cachettl time.Duration @@ -99,7 +104,7 @@ func (msg *PssMsg) serialize() []byte { } -var pssTransportProtocol = &protocols.Spec{ +var pssSpec = &protocols.Spec{ Name: "pss", Version: 1, MaxMsgSize: 10 * 1024 * 1024, @@ -188,16 +193,16 @@ func (self *Pss) Stop() error { func (self *Pss) Protocols() []p2p.Protocol { return []p2p.Protocol{ p2p.Protocol{ - Name: pssTransportProtocol.Name, - Version: pssTransportProtocol.Version, - Length: pssTransportProtocol.Length(), + Name: pssSpec.Name, + Version: pssSpec.Version, + Length: pssSpec.Length(), Run: self.Run, }, } } func (self *Pss) Run(p *p2p.Peer, rw p2p.MsgReadWriter) error { - pp := protocols.NewPeer(p, rw, pssTransportProtocol) + pp := protocols.NewPeer(p, rw, pssSpec) return pp.Run(self.handlePssMsg) } @@ -371,13 +376,13 @@ func (self *Pss) Forward(msg *PssMsg) error { // send with kademlia // find the closest peer to the recipient and attempt to send self.Overlay.EachConn(msg.To, 256, func(op network.OverlayConn, po int, isproxbin bool) bool { - p, ok := op.(network.Peer) + p, ok := op.(senderPeer) if !ok { return true } addr := self.Overlay.BaseAddr() - sendMsg := fmt.Sprintf("%x: msg to %x via %x", common.ByteLabel(addr), common.ByteLabel(msg.To), common.ByteLabel(p.Over())) - if self.checkFwdCache(p.Over(), digest) { + sendMsg := fmt.Sprintf("%x: msg to %x via %x", common.ByteLabel(addr), common.ByteLabel(msg.To), common.ByteLabel(p.Address())) + if self.checkFwdCache(p.Address(), digest) { log.Info(fmt.Sprintf("%v: peer already forwarded to", sendMsg)) return true } @@ -389,10 +394,10 @@ func (self *Pss) Forward(msg *PssMsg) error { log.Trace(fmt.Sprintf("%v: successfully forwarded", sendMsg)) sent++ // if equality holds, p is always the first peer given in the iterator - if bytes.Equal(msg.To, p.Over()) || !isproxbin { + if bytes.Equal(msg.To, p.Address()) || !isproxbin { return false } - log.Trace(fmt.Sprintf("%x is in proxbin, keep forwarding", common.ByteLabel(p.Over()))) + log.Trace(fmt.Sprintf("%x is in proxbin, keep forwarding", common.ByteLabel(p.Address()))) return true }) diff --git a/swarm/pss/pss_test.go b/swarm/pss/pss_test.go index 1a02a70067..d9849d042a 100644 --- a/swarm/pss/pss_test.go +++ b/swarm/pss/pss_test.go @@ -13,6 +13,7 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/p2p/protocols" "github.com/ethereum/go-ethereum/p2p/simulations" "github.com/ethereum/go-ethereum/p2p/simulations/adapters" p2ptest "github.com/ethereum/go-ethereum/p2p/testing" @@ -131,7 +132,7 @@ func TestPssRegisterHandler(t *testing.T) { ps := newTestPss(addr.OAddr) from := network.RandomAddr() payload := []byte("payload") - topic := NewTopic(pssTransportProtocol.Name, int(pssTransportProtocol.Version)) + topic := NewTopic(pssSpec.Name, int(pssSpec.Version)) wrongtopic := NewTopic("foo", 42) checkMsg := func(msg []byte, p *p2p.Peer, sender []byte) error { if !bytes.Equal(from.OAddr, sender) { @@ -172,23 +173,36 @@ func TestPssRegisterHandler(t *testing.T) { } func TestPssSimpleLinear(t *testing.T) { + var err error nodeconfig := adapters.RandomNodeConfig() addr := network.NewAddrFromNodeId(nodeconfig.Id) _ = p2ptest.NewTestPeerPool() - ps := newTestPss(addr.OAddr) + ps := newTestPss(addr.Over()) ping := &pssPing{ quitC: make(chan struct{}), } - err := RegisterPssProtocol(ps, &pssPingTopic, pssPingProtocol, newPssPingProtocol(ping.pssPingHandler)) + err = RegisterPssProtocol(ps, &pssPingTopic, pssPingProtocol, newPssPingProtocol(ping.pssPingHandler)) if err != nil { t.Fatalf("Failed to register virtual protocol in pss: %v", err) } - pt := p2ptest.NewProtocolTester(t, nodeconfig.Id, 2, ps.Run) + run := func(p *p2p.Peer, rw p2p.MsgReadWriter) error { + id := p.ID() + bp := &testPssPeer{ + Peer: protocols.NewPeer(p, rw, pssSpec), + addr: network.ToOverlayAddr(id[:]), + } + ps.Overlay.On(bp) + defer ps.Overlay.Off(bp) + log.Debug(fmt.Sprintf("%v", ps.Overlay)) + return bp.Run(ps.handlePssMsg) + } - msg := newPssPingMsg(ps, pssPingProtocol, pssPingTopic, []byte{1, 2, 3}) + pt := p2ptest.NewProtocolTester(t, nodeconfig.Id, 2, run) + + msg := newPssPingMsg(ps, network.ToOverlayAddr(pt.Ids[0].Bytes()), pssPingProtocol, pssPingTopic, []byte{1, 2, 3}) exchange := p2ptest.Exchange{ Expects: []p2ptest.Expect{ @@ -207,7 +221,10 @@ func TestPssSimpleLinear(t *testing.T) { }, } - pt.TestExchanges(exchange) + err = pt.TestExchanges(exchange) + if err != nil { + t.Fatalf("exchange failed %v", err) + } } func TestPssFullRandom10_5_5(t *testing.T) { @@ -367,7 +384,7 @@ func newServices() adapters.Services { kadparams.MaxRetries = 1000 kadparams.RetryExponent = 2 kadparams.RetryInterval = 1000000 - kademlia := network.NewKademlia(addr.OAddr, kadparams) + kademlia := network.NewKademlia(addr.Over(), kadparams) config := &network.BzzConfig{ OverlayAddr: addr.Over(), From c15eba20ddb83b02186f9898bfd269a00b24bd11 Mon Sep 17 00:00:00 2001 From: nolash Date: Thu, 18 May 2017 08:23:28 +0200 Subject: [PATCH 24/24] swarm/network: minor bug in kademlia method --- swarm/network/kademlia.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/swarm/network/kademlia.go b/swarm/network/kademlia.go index dc8faa6d0d..6be4feebf9 100644 --- a/swarm/network/kademlia.go +++ b/swarm/network/kademlia.go @@ -506,7 +506,7 @@ func NewPeerPot(kadMinProxSize int, ids ...*adapters.NodeId) map[discover.NodeID // create a table of all nodes for health check np := pot.NewPot(nil, 0) for _, id := range ids { - o := ToOverlayAddr(id) + o := ToOverlayAddr(id.Bytes()) np, _, _ = pot.Add(np, pot.NewBytesVal(o, nil)) } nnmap := make(map[discover.NodeID][][]byte)