p2p/simulations: improvement and adding msg events

* Conceals rw in messenger type
* Adds generic remote control for nodes in network sim
* Adds msg type and event to sim, journal
* Msg http output, http options handler
* Msg event added to "cytoscape" for output to visualizer
* Msg event structure changed to fit "cytodata" logic
* Fix for handling CORS/PUT requests:
* fallthrough OPTIONS method handler
* Access-Control-Allow-Methods header added (note that the servermux panics if sent empty method value through http)
This commit is contained in:
nolash 2017-02-06 23:44:45 +01:00 committed by Lewis Marshall
parent 3c84c329f9
commit 9822d7405e
14 changed files with 403 additions and 86 deletions

View file

@ -25,16 +25,17 @@ import (
"github.com/ethereum/go-ethereum/p2p/discover" "github.com/ethereum/go-ethereum/p2p/discover"
) )
func newPeer(rw p2p.MsgReadWriter) *Peer { func newPeer(m Messenger) *Peer {
return &Peer{ return &Peer{
RW: rw, Messenger: m,
Errc: make(chan error, 1), Errc: make(chan error, 1),
Flushc: make(chan bool), Flushc: make(chan bool),
} }
} }
type Peer struct { type Peer struct {
RW p2p.MsgReadWriter //RW p2p.MsgReadWriter
Messenger
Errc chan error Errc chan error
Flushc chan bool Flushc chan bool
} }
@ -51,17 +52,17 @@ type SimNode struct {
lock sync.RWMutex lock sync.RWMutex
Id *NodeId Id *NodeId
network Network network Network
messenger Messenger messenger func(p2p.MsgReadWriter) Messenger
peerMap map[discover.NodeID]int peerMap map[discover.NodeID]int
peers []*Peer peers []*Peer
Run ProtoCall Run ProtoCall
} }
func (self *SimNode) Messenger() Messenger { func (self *SimNode) Messenger(rw p2p.MsgReadWriter) Messenger {
return self.messenger return self.messenger(rw)
} }
func NewSimNode(id *NodeId, n Network, m Messenger) *SimNode { func NewSimNode(id *NodeId, n Network, m func(p2p.MsgReadWriter) Messenger) *SimNode {
return &SimNode{ return &SimNode{
Id: id, Id: id,
network: n, network: n,
@ -95,24 +96,24 @@ func (self *SimNode) getPeer(id *NodeId) *Peer {
func (self *SimNode) SetPeer(id *NodeId, rw p2p.MsgReadWriter) { func (self *SimNode) SetPeer(id *NodeId, rw p2p.MsgReadWriter) {
self.lock.Lock() self.lock.Lock()
defer self.lock.Unlock() defer self.lock.Unlock()
self.setPeer(id, rw) self.setPeer(id, self.Messenger(rw))
} }
func (self *SimNode) setPeer(id *NodeId, rw p2p.MsgReadWriter) *Peer { func (self *SimNode) setPeer(id *NodeId, m Messenger) *Peer {
i, found := self.peerMap[id.NodeID] i, found := self.peerMap[id.NodeID]
if !found { if !found {
i = len(self.peers) i = len(self.peers)
self.peerMap[id.NodeID] = i self.peerMap[id.NodeID] = i
p := newPeer(rw) p := newPeer(m)
self.peers = append(self.peers, p) self.peers = append(self.peers, p)
return p return p
} }
if self.peers[i] != nil && rw != nil { if self.peers[i] != nil && m != nil {
panic(fmt.Sprintf("pipe for %v already set", id)) panic(fmt.Sprintf("pipe for %v already set", id))
} }
// legit reconnect reset disconnection error, // legit reconnect reset disconnection error,
p := self.peers[i] p := self.peers[i]
p.RW = rw p.Messenger = m
return p return p
} }
@ -121,11 +122,11 @@ func (self *SimNode) Disconnect(rid []byte) error {
defer self.lock.Unlock() defer self.lock.Unlock()
id := NewNodeId(rid) id := NewNodeId(rid)
peer := self.getPeer(id) peer := self.getPeer(id)
if peer == nil || peer.RW == nil { if peer == nil || peer.Messenger == nil {
return fmt.Errorf("already disconnected") return fmt.Errorf("already disconnected")
} }
peer.RW.(*p2p.MsgPipeRW).Close() peer.Messenger.Close()
peer.RW = nil peer.Messenger = nil
// na := self.network.GetNodeAdapter(id) // na := self.network.GetNodeAdapter(id)
// peer = na.(*SimNode).GetPeer(self.Id) // peer = na.(*SimNode).GetPeer(self.Id)
// peer.RW = nil // peer.RW = nil
@ -146,12 +147,12 @@ func (self *SimNode) Connect(rid []byte) error {
defer close(runc) defer close(runc)
// run protocol on remote node with self as peer // run protocol on remote node with self as peer
err := na.(*SimNode).runProtocol(self.Id, rrw, rw, runc) err := na.(ProtocolRunner).RunProtocol(self.Id, rrw, rw, runc)
if err != nil { if err != nil {
return fmt.Errorf("cannot run protocol (%v -> %v) %v", self.Id, id, err) return fmt.Errorf("cannot run protocol (%v -> %v) %v", self.Id, id, err)
} }
// run protocol on remote node with self as peer // run protocol on remote node with self as peer
err = self.runProtocol(id, rw, rrw, runc) err = self.RunProtocol(id, rw, rrw, runc)
if err != nil { if err != nil {
return fmt.Errorf("cannot run protocol (%v -> %v): %v", id, self.Id, err) return fmt.Errorf("cannot run protocol (%v -> %v): %v", id, self.Id, err)
} }
@ -159,17 +160,17 @@ func (self *SimNode) Connect(rid []byte) error {
return nil return nil
} }
func (self *SimNode) runProtocol(id *NodeId, rw, rrw p2p.MsgReadWriter, runc chan bool) error { func (self *SimNode) RunProtocol(id *NodeId, rw, rrw p2p.MsgReadWriter, runc chan bool) error {
if self.Run == nil { if self.Run == nil {
glog.V(6).Infof("no protocol starting on peer %v (connection with %v)", self.Id, id) glog.V(6).Infof("no protocol starting on peer %v (connection with %v)", self.Id, id)
return nil return nil
} }
glog.V(6).Infof("protocol starting on peer %v (connection with %v)", self.Id, id) glog.V(6).Infof("protocol starting on peer %v (connection with %v)", self.Id, id)
peer := self.getPeer(id) peer := self.getPeer(id)
if peer != nil && peer.RW != nil { if peer != nil && peer.Messenger != nil {
return fmt.Errorf("already connected %v to peer %v", self.Id, id) return fmt.Errorf("already connected %v to peer %v", self.Id, id)
} }
peer = self.setPeer(id, rrw) peer = self.setPeer(id, self.Messenger(rrw))
p := p2p.NewPeer(id.NodeID, Name(id.Bytes()), []p2p.Cap{}) p := p2p.NewPeer(id.NodeID, Name(id.Bytes()), []p2p.Cap{})
go func() { go func() {
err := self.Run(p, rw) err := self.Run(p, rw)

View file

@ -31,20 +31,30 @@ import (
// peer session test // peer session test
// ExpectMsg(p2p.MsgReader, uint64, interface{}) error // ExpectMsg(p2p.MsgReader, uint64, interface{}) error
// SendMsg(p2p.MsgWriter, uint64, interface{}) error // SendMsg(p2p.MsgWriter, uint64, interface{}) error
type SimPipe struct{} type SimPipe struct{
rw p2p.MsgReadWriter
func (*SimPipe) SendMsg(w p2p.MsgWriter, code uint64, msg interface{}) error {
return p2p.Send(w, code, msg)
} }
func (*SimPipe) ReadMsg(r p2p.MsgReader) (p2p.Msg, error) { func (self *SimPipe) SendMsg(code uint64, msg interface{}) error {
return r.ReadMsg() return p2p.Send(self.rw, code, msg)
} }
func (*SimPipe) TriggerMsg(w p2p.MsgWriter, code uint64, msg interface{}) error { func (self *SimPipe) ReadMsg() (p2p.Msg, error) {
return p2p.Send(w, code, msg) return self.rw.ReadMsg()
} }
func (*SimPipe) ExpectMsg(r p2p.MsgReader, code uint64, msg interface{}) error { func (self *SimPipe) TriggerMsg(code uint64, msg interface{}) error {
return p2p.ExpectMsg(r, code, msg) return p2p.Send(self.rw, code, msg)
}
func (self *SimPipe) ExpectMsg(code uint64, msg interface{}) error {
return p2p.ExpectMsg(self.rw, code, msg)
}
func (self *SimPipe) Close() {
self.rw.(*p2p.MsgPipeRW).Close()
}
func NewSimPipe(rw p2p.MsgReadWriter) Messenger {
return Messenger(&SimPipe{rw})
} }

View file

@ -19,6 +19,7 @@ package adapters
import ( import (
"fmt" "fmt"
"net" "net"
//"encoding/binary"
"github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/p2p/discover" "github.com/ethereum/go-ethereum/p2p/discover"
@ -30,17 +31,19 @@ type RLPx struct {
id *NodeId id *NodeId
net *p2p.Server net *p2p.Server
addr []byte addr []byte
m Messenger m func(p2p.MsgReadWriter) Messenger
r Reporter r Reporter
} }
type RLPxMessenger struct { type RLPxMessenger struct {
rw p2p.MsgReadWriter
} }
func NewRLPx(addr []byte, srv *p2p.Server, m Messenger) *RLPx { func NewRLPxMessenger(rw p2p.MsgReadWriter) Messenger {
if m == nil { return Messenger(&RLPxMessenger{rw: rw})
m = &RLPxMessenger{}
} }
func NewRLPx(addr []byte, srv *p2p.Server, m func(p2p.MsgReadWriter) Messenger) *RLPx {
return &RLPx{ return &RLPx{
net: srv, net: srv,
addr: addr, addr: addr,
@ -48,7 +51,7 @@ func NewRLPx(addr []byte, srv *p2p.Server, m Messenger) *RLPx {
} }
} }
func NewReportingRLPx(addr []byte, srv *p2p.Server, m Messenger, r Reporter) *RLPx { func NewReportingRLPx(addr []byte, srv *p2p.Server, m func(p2p.MsgReadWriter) Messenger, r Reporter) *RLPx {
rlpx := NewRLPx(addr, srv, m) rlpx := NewRLPx(addr, srv, m)
rlpx.r = r rlpx.r = r
srv.PeerConnHook = func(p *p2p.Peer) { srv.PeerConnHook = func(p *p2p.Peer) {
@ -60,12 +63,16 @@ func NewReportingRLPx(addr []byte, srv *p2p.Server, m Messenger, r Reporter) *RL
return rlpx return rlpx
} }
func (RLPxMessenger) SendMsg(w p2p.MsgWriter, code uint64, msg interface{}) error { func (self *RLPxMessenger) SendMsg(code uint64, msg interface{}) error {
return p2p.Send(w, code, msg) return p2p.Send(self.rw, code, msg)
} }
func (RLPxMessenger) ReadMsg(r p2p.MsgReader) (p2p.Msg, error) { func (self *RLPxMessenger) ReadMsg() (p2p.Msg, error) {
return r.ReadMsg() return self.rw.ReadMsg()
}
func (self *RLPxMessenger) Close() {
} }
func (self *RLPx) LocalAddr() []byte { func (self *RLPx) LocalAddr() []byte {
@ -83,12 +90,15 @@ func (self *RLPx) Connect(enode []byte) error {
return nil return nil
} }
func (self *RLPx) Messenger() Messenger { func (self *RLPx) Messenger(rw p2p.MsgReadWriter) Messenger {
return self.m return self.m(rw)
} }
func (self *RLPx) Disconnect(p *p2p.Peer, rw p2p.MsgReadWriter) error { //func (self *RLPx) Disconnect(p *p2p.Peer, rw p2p.MsgReadWriter) error {
p.Disconnect(p2p.DiscSubprotocolError) func (self *RLPx) Disconnect(b []byte) error {
//p.Disconnect(p2p.DiscSubprotocolError)
//d, _ := binary.Uvarint(b)
//p.Disconnect(p2p.DiscReason(d))
return nil return nil
} }

View file

@ -17,6 +17,7 @@
package adapters package adapters
import ( import (
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/p2p/discover" "github.com/ethereum/go-ethereum/p2p/discover"
) )
@ -63,8 +64,9 @@ func (self *NodeId) Label() string {
} }
type Messenger interface { type Messenger interface {
SendMsg(p2p.MsgWriter, uint64, interface{}) error SendMsg(uint64, interface{}) error
ReadMsg(p2p.MsgReader) (p2p.Msg, error) ReadMsg() (p2p.Msg, error)
Close()
} }
type NodeAdapter interface { type NodeAdapter interface {
@ -73,7 +75,12 @@ type NodeAdapter interface {
// Disconnect(*p2p.Peer, p2p.MsgReadWriter) // Disconnect(*p2p.Peer, p2p.MsgReadWriter)
LocalAddr() []byte LocalAddr() []byte
ParseAddr([]byte, string) ([]byte, error) ParseAddr([]byte, string) ([]byte, error)
Messenger() Messenger // Messenger() Messenger <<<... old version
Messenger(p2p.MsgReadWriter) Messenger
}
type ProtocolRunner interface {
RunProtocol(id *NodeId, rw, rrw p2p.MsgReadWriter, runc chan bool) error
} }
type StartAdapter interface { type StartAdapter interface {
@ -85,3 +92,23 @@ type Reporter interface {
DidConnect(*NodeId, *NodeId) error DidConnect(*NodeId, *NodeId) error
DidDisconnect(*NodeId, *NodeId) error DidDisconnect(*NodeId, *NodeId) error
} }
func RandomNodeId() *NodeId {
key, err := crypto.GenerateKey()
if err != nil {
panic("unable to generate key")
}
var id discover.NodeID
pubkey := crypto.FromECDSAPub(&key.PublicKey)
copy(id[:], pubkey[1:])
return &NodeId{id}
}
func RandomNodeIds(n int) []*NodeId {
var ids []*NodeId
for i := 0; i < n; i++ {
ids = append(ids, RandomNodeId())
}
return ids
}

View file

@ -122,6 +122,11 @@ type CodeMap struct {
messages map[reflect.Type]uint64 // index of types to codes, for sending by type messages map[reflect.Type]uint64 // index of types to codes, for sending by type
} }
func (self *CodeMap) GetCode(msg interface{}) (uint64, bool) {
code, found := self.messages[reflect.TypeOf(msg)]
return code, found
}
func NewCodeMap(name string, version uint, maxMsgSize int, msgs ...interface{}) *CodeMap { func NewCodeMap(name string, version uint, maxMsgSize int, msgs ...interface{}) *CodeMap {
self := &CodeMap{ self := &CodeMap{
Name: name, Name: name,
@ -133,10 +138,6 @@ func NewCodeMap(name string, version uint, maxMsgSize int, msgs ...interface{})
return self return self
} }
func (self *CodeMap) GetCode(msg interface{}) uint64 {
return self.messages[reflect.TypeOf(msg)]
}
func (self *CodeMap) Length() uint64 { func (self *CodeMap) Length() uint64 {
return uint64(len(self.codes)) return uint64(len(self.codes))
} }
@ -157,6 +158,26 @@ func (self *CodeMap) Register(msgs ...interface{}) {
} }
} }
func NewProtocol(protocolname string, protocolversion uint, run func(*Peer) error, na adapters.NodeAdapter, ct *CodeMap) *p2p.Protocol {
r := func(p *p2p.Peer, rw p2p.MsgReadWriter) error {
m := na.Messenger(rw)
peer := NewPeer(p, ct, m, func() {})
return run(peer)
}
return &p2p.Protocol{
Name: protocolname,
Version: protocolversion,
Length: ct.Length(),
Run: r,
}
}
// A Peer represents a remote peer or protocol instance that is running on a peer connection with // A Peer represents a remote peer or protocol instance that is running on a peer connection with
// a remote peer // a remote peer
type Peer struct { type Peer struct {
@ -172,12 +193,11 @@ type Peer struct {
// this constructor is called by the p2p.Protocol#Run function // 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 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 // the third argument is the CodeMap describing the protocol messages and options
func NewPeer(p *p2p.Peer, rw p2p.MsgReadWriter, ct *CodeMap, m adapters.Messenger, disconn func()) *Peer { func NewPeer(p *p2p.Peer, ct *CodeMap, m adapters.Messenger, disconn func()) *Peer {
return &Peer{ return &Peer{
ct: ct, ct: ct,
m: m, m: m,
Peer: p, Peer: p,
rw: rw,
handlers: make(map[reflect.Type][]func(interface{}) error), handlers: make(map[reflect.Type][]func(interface{}) error),
disconnect: disconn, disconnect: disconn,
} }
@ -229,13 +249,12 @@ func (self *Peer) Drop() {
// this low level call will be wrapped by libraries providing routed or broadcast sends // 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 // but often just used to forward and push messages to directly connected peers
func (self *Peer) Send(msg interface{}) error { func (self *Peer) Send(msg interface{}) error {
typ := reflect.TypeOf(msg) code, found := self.ct.GetCode(msg)
code, found := self.ct.messages[typ]
if !found { if !found {
return errorf(ErrInvalidMsgType, "%v", typ) return errorf(ErrInvalidMsgType, "%v", code)
} }
glog.V(logger.Debug).Infof("=> %v %v (%d)", msg, typ, code) glog.V(logger.Debug).Infof("=> %v (%d)", msg, code)
err := self.m.SendMsg(self.rw, uint64(code), msg) err := self.m.SendMsg(uint64(code), msg)
if err != nil { if err != nil {
self.Drop() self.Drop()
return errorf(ErrWrite, "(msg code: %v): %v", code, err) return errorf(ErrWrite, "(msg code: %v): %v", code, err)
@ -249,7 +268,7 @@ func (self *Peer) Send(msg interface{}) error {
// checks message size, out-of-range message codes, handles decoding with reflection, // checks message size, out-of-range message codes, handles decoding with reflection,
// call handlers as callback onside // call handlers as callback onside
func (self *Peer) handleIncoming() (interface{}, error) { func (self *Peer) handleIncoming() (interface{}, error) {
msg, err := self.m.ReadMsg(self.rw) msg, err := self.m.ReadMsg()
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -64,7 +64,7 @@ func newProtocol(pp *p2ptest.TestPeerPool, wg *sync.WaitGroup) func(adapters.Nod
wg.Add(1) wg.Add(1)
} }
id := &adapters.NodeId{p.ID()} id := &adapters.NodeId{p.ID()}
peer := NewPeer(p, rw, ct, na.Messenger(), func() { na.Disconnect(id.Bytes()) }) peer := NewPeer(p, ct, na.Messenger(rw), func() { na.Disconnect(id.Bytes()) })
// demonstrates use of peerPool, killing another peer connection as a response to a message // demonstrates use of peerPool, killing another peer connection as a response to a message
peer.Register(&kill{}, func(msg interface{}) error { peer.Register(&kill{}, func(msg interface{}) error {

View file

@ -30,11 +30,13 @@ type CyElement struct {
type CyUpdate struct { type CyUpdate struct {
Add []*CyElement `json:"add"` Add []*CyElement `json:"add"`
Remove []string `json:"remove"` Remove []string `json:"remove"`
Message []string `json:"message"`
} }
func UpdateCy(conf *CyConfig, j *Journal) (*CyUpdate, error) { func UpdateCy(conf *CyConfig, j *Journal) (*CyUpdate, error) {
added := []*CyElement{} added := []*CyElement{}
removed := []string{} removed := []string{}
messaged := []string{}
var el *CyElement var el *CyElement
update := func(e *event.Event) bool { update := func(e *event.Event) bool {
entry := e.Data entry := e.Data
@ -42,6 +44,14 @@ func UpdateCy(conf *CyConfig, j *Journal) (*CyUpdate, error) {
if ev, ok := entry.(*NodeEvent); ok { if ev, ok := entry.(*NodeEvent); ok {
el = &CyElement{Group: "nodes", Data: &CyData{Id: ev.node.Id.Label()}} el = &CyElement{Group: "nodes", Data: &CyData{Id: ev.node.Id.Label()}}
action = ev.Action action = ev.Action
} else if ev, ok := entry.(*MsgEvent); ok {
msg := ev.msg
id := ConnLabel(msg.One, msg.Other)
var source, target string
source = msg.One.Label()
target = msg.Other.Label()
el = &CyElement{Group: "msgs", Data: &CyData{Id: id, Source: source, Target: target}}
action = ev.Action
} else if ev, ok := entry.(*ConnEvent); ok { } else if ev, ok := entry.(*ConnEvent); ok {
// mutually exclusive directed edge (caller -> callee) // mutually exclusive directed edge (caller -> callee)
conn := ev.conn conn := ev.conn
@ -67,6 +77,9 @@ func UpdateCy(conf *CyConfig, j *Journal) (*CyUpdate, error) {
case "down": case "down":
el.Data.Up = false el.Data.Up = false
removed = append(removed, el.Data.Id) removed = append(removed, el.Data.Id)
case "msg":
el.Data.Up = true
messaged = append(messaged, el.Data.Id)
default: default:
panic("unknown action") panic("unknown action")
} }
@ -77,5 +90,6 @@ func UpdateCy(conf *CyConfig, j *Journal) (*CyUpdate, error) {
return &CyUpdate{ return &CyUpdate{
Add: added, Add: added,
Remove: removed, Remove: removed,
Message: messaged,
}, nil }, nil
} }

View file

@ -33,10 +33,15 @@ import (
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/p2p/adapters" "github.com/ethereum/go-ethereum/p2p/adapters"
"github.com/ethereum/go-ethereum/p2p/discover" "github.com/ethereum/go-ethereum/p2p/discover"
) )
type NetworkQuery struct {
Type string
}
type NetworkConfig struct { type NetworkConfig struct {
// Type NetworkType // Type NetworkType
// Config json.RawMessage // type-specific configs // Config json.RawMessage // type-specific configs
@ -47,7 +52,7 @@ type NetworkConfig struct {
// event types related to connectivity, i.e., nodes coming on dropping off // event types related to connectivity, i.e., nodes coming on dropping off
// and connections established and dropped // and connections established and dropped
var ConnectivityEvents = []interface{}{&NodeEvent{}, &ConnEvent{}} var ConnectivityEvents = []interface{}{&NodeEvent{}, &ConnEvent{}, &MsgEvent{}}
// NewNetworkController creates a ResourceController responding to GET and DELETE methods // NewNetworkController creates a ResourceController responding to GET and DELETE methods
// it embeds a mockers controller, a journal player, node and connection contollers. // it embeds a mockers controller, a journal player, node and connection contollers.
@ -55,6 +60,7 @@ var ConnectivityEvents = []interface{}{&NodeEvent{}, &ConnEvent{}}
// Events from the eventer go into the provided journal. The content of the journal can be // Events from the eventer go into the provided journal. The content of the journal can be
// accessed through the HTTP API. // accessed through the HTTP API.
func NewNetworkController(conf *NetworkConfig, eventer *event.TypeMux, journal *Journal) Controller { func NewNetworkController(conf *NetworkConfig, eventer *event.TypeMux, journal *Journal) Controller {
self := NewResourceContoller( self := NewResourceContoller(
&ResourceHandlers{ &ResourceHandlers{
// GET /<networkId>/ // GET /<networkId>/
@ -103,6 +109,7 @@ type Network struct {
connMap map[string]int connMap map[string]int
Nodes []*Node `json:"nodes"` Nodes []*Node `json:"nodes"`
Conns []*Conn `json:"conns"` Conns []*Conn `json:"conns"`
messenger func(p2p.MsgReadWriter) adapters.Messenger
// //
// adapters.Messenger // adapters.Messenger
// node adapter function that creates the node model for // node adapter function that creates the node model for
@ -116,6 +123,7 @@ func NewNetwork(triggers, events *event.TypeMux) *Network {
events: events, events: events,
nodeMap: make(map[discover.NodeID]int), nodeMap: make(map[discover.NodeID]int),
connMap: make(map[string]int), connMap: make(map[string]int),
messenger: adapters.NewSimPipe,
} }
} }
@ -155,6 +163,12 @@ type ConnEvent struct {
conn *Conn conn *Conn
} }
type MsgEvent struct {
Action string
Type string
msg *Msg
}
func (self *ConnEvent) String() string { func (self *ConnEvent) String() string {
return fmt.Sprintf("<Action: %v, Type: %v, Data: %v>\n", self.Action, self.Type, self.conn) return fmt.Sprintf("<Action: %v, Type: %v, Data: %v>\n", self.Action, self.Type, self.conn)
} }
@ -163,6 +177,10 @@ func (self *NodeEvent) String() string {
return fmt.Sprintf("<Action: %v, Type: %v, Data: %v>\n", self.Action, self.Type, self.node) return fmt.Sprintf("<Action: %v, Type: %v, Data: %v>\n", self.Action, self.Type, self.node)
} }
func (self *MsgEvent) String() string {
return fmt.Sprintf("<Action: %v, Type: %v, Data: %v>\n", self.Action, self.Type, self.msg)
}
func (self *Node) event(up bool) *NodeEvent { func (self *Node) event(up bool) *NodeEvent {
var action string var action string
if up { if up {
@ -210,6 +228,25 @@ func (self *Conn) event(up, rev bool) *ConnEvent {
} }
} }
type Msg struct {
One *adapters.NodeId `json:"one"`
Other *adapters.NodeId `json:"other"`
Code uint64 `json:"conn"`
}
func (self *Msg) String() string {
return fmt.Sprintf("Msg(%d) %v->%v", self.Code, self.One.Label(), self.Other.Label())
}
func (self *Msg) event() *MsgEvent {
return &MsgEvent{
Action: "up",
//Type: fmt.Sprintf("%d", self.Code),
Type: "msg",
msg: self,
}
}
type NodeConfig struct { type NodeConfig struct {
Id *adapters.NodeId `json:"Id"` Id *adapters.NodeId `json:"Id"`
} }
@ -253,6 +290,12 @@ func (self *Network) NewNode(conf *NodeConfig) error {
return nil return nil
} }
func (self *Network) NewGenericSimNode(conf *NodeConfig) adapters.NodeAdapter {
id := conf.Id
na := adapters.NewSimNode(id, self, self.messenger)
return na
}
// newConn adds a new connection to the network // newConn adds a new connection to the network
// it errors if the respective nodes do not exist // it errors if the respective nodes do not exist
func (self *Network) newConn(oneId, otherId *adapters.NodeId) (*Conn, error) { func (self *Network) newConn(oneId, otherId *adapters.NodeId) (*Conn, error) {
@ -440,6 +483,17 @@ func (self *Network) DidDisconnect(one, other *adapters.NodeId) error {
return nil return nil
} }
// Send(senderid, receiverid) sends a message from one node to another
func (self *Network) Send(senderid, receiverid *adapters.NodeId, msgcode uint64, protomsg interface{}) {
msg := &Msg{
One: senderid,
Other: receiverid,
Code: msgcode,
}
//self.GetNode(senderid).na.(*adapters.SimNode).GetPeer(receiverid).SendMsg(msgcode, protomsg) // phew!
self.events.Post(msg.event()) // should also include send status maybe
}
// GetNodeAdapter(id) returns the NodeAdapter for node with id // GetNodeAdapter(id) returns the NodeAdapter for node with id
// returns nil if node does not exist // returns nil if node does not exist
func (self *Network) GetNodeAdapter(id *adapters.NodeId) adapters.NodeAdapter { func (self *Network) GetNodeAdapter(id *adapters.NodeId) adapters.NodeAdapter {

View file

@ -38,6 +38,7 @@ func handle(w http.ResponseWriter, r *http.Request, c Controller) {
uri := requestURL.Path uri := requestURL.Path
w.Header().Set("Content-Type", "text/json") w.Header().Set("Content-Type", "text/json")
w.Header().Set("Access-Control-Allow-Origin", "*") w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
defer r.Body.Close() defer r.Body.Close()
parts := strings.Split(uri, "/") parts := strings.Split(uri, "/")
var err error var err error

View file

@ -13,6 +13,7 @@ import (
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/p2p/adapters" "github.com/ethereum/go-ethereum/p2p/adapters"
) )
type returnHandler func(body io.Reader) (resp io.ReadSeeker, err error) type returnHandler func(body io.Reader) (resp io.ReadSeeker, err error)
@ -34,6 +35,16 @@ type ResourceController struct {
*ResourceHandlers *ResourceHandlers
} }
type NodeResult struct {
Nodes []*Node
}
type NodeIF struct {
One uint
Other uint
MessageType uint8
}
var methodsAvailable = []string{"POST", "GET", "PUT", "DELETE"} var methodsAvailable = []string{"POST", "GET", "PUT", "DELETE"}
func (self *ResourceHandlers) handler(method string) *ResourceHandler { func (self *ResourceHandlers) handler(method string) *ResourceHandler {
@ -47,7 +58,14 @@ func (self *ResourceHandlers) handler(method string) *ResourceHandler {
h = self.Update h = self.Update
case "DELETE": case "DELETE":
h = self.Destroy h = self.Destroy
case "OPTIONS":
h = &ResourceHandler{
Handle: func(msg interface{}, c *ResourceController) (interface{}, error) {
return struct{}{}, nil
},
} }
}
return h return h
} }
@ -75,7 +93,10 @@ func NewSessionController() (*ResourceController, chan bool) {
Create: &ResourceHandler{ Create: &ResourceHandler{
Handle: func(msg interface{}, parent *ResourceController) (interface{}, error) { Handle: func(msg interface{}, parent *ResourceController) (interface{}, error) {
conf := msg.(*NetworkConfig) conf := msg.(*NetworkConfig)
m := NewNetworkController(conf, &event.TypeMux{}, NewJournal()) journal := NewJournal()
net := NewNetwork(nil, &event.TypeMux{})
net.SetNaf(net.NewGenericSimNode)
m := NewNetworkController(conf, net.Events(), journal)
if len(conf.Id) == 0 { if len(conf.Id) == 0 {
conf.Id = fmt.Sprintf("%d", parent.id) conf.Id = fmt.Sprintf("%d", parent.id)
} }
@ -84,6 +105,66 @@ func NewSessionController() (*ResourceController, chan bool) {
parent.SetResource(conf.Id, m) parent.SetResource(conf.Id, m)
} }
parent.id++ parent.id++
m.SetResource("debug", NewResourceContoller(
&ResourceHandlers{
Create: &ResourceHandler{
Handle: func(msg interface{}, parent *ResourceController) (interface{}, error) {
journaldump := []string{}
eventfmt := func(e *event.Event) bool {
journaldump = append(journaldump, fmt.Sprintf("%v", e))
return true
}
journal.Read(eventfmt)
return struct{Results []string}{Results: journaldump,}, nil
},
},
},
))
m.SetResource("node", NewResourceContoller(
&ResourceHandlers{
Create: &ResourceHandler{
Handle: func(msg interface{}, parent *ResourceController) (interface{}, error) {
var nodeid *adapters.NodeId
nodeid = adapters.RandomNodeId()
net.NewNode(&NodeConfig{Id: nodeid})
glog.V(6).Infof("added node %v to network %v", nodeid, net)
return &NodeConfig{Id: nodeid}, nil
},
},
Retrieve: &ResourceHandler{
Handle: func(msg interface{}, parent *ResourceController) (interface{}, error) {
return &NodeResult{Nodes: net.Nodes}, nil
},
},
Update: &ResourceHandler{
Handle: func(msg interface{}, parent *ResourceController) (interface{}, error) {
var othernode *Node
args := msg.(*NodeIF)
onenode := net.Nodes[args.One - 1]
if args.Other == 0 {
if net.Start(onenode.Id) != nil {
net.Stop(onenode.Id)
}
return &NodeResult{Nodes: []*Node{onenode}}, nil
} else {
othernode = net.Nodes[args.Other - 1]
net.Connect(onenode.Id, othernode.Id)
return &NodeResult{Nodes: []*Node{onenode, othernode}}, nil
}
},
Type: reflect.TypeOf(&NodeIF{}), // this is input not output param structure
},
},
))
return empty, nil return empty, nil
}, },
Type: reflect.TypeOf(&NetworkConfig{}), Type: reflect.TypeOf(&NetworkConfig{}),

View file

@ -8,12 +8,16 @@ import (
"net/http" "net/http"
"testing" "testing"
"time" "time"
"encoding/json"
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/p2p/adapters" "github.com/ethereum/go-ethereum/p2p/adapters"
) )
/***
* \todo rewrite this with a scripting engine to do http protocol xchanges more easily
*/
const ( const (
domain = "http://localhost" domain = "http://localhost"
port = "8888" port = "8888"
@ -55,8 +59,61 @@ func TestDelete(t *testing.T) {
} }
func TestCreate(t *testing.T) { func TestCreate(t *testing.T) {
// should test that session controller POST creates network controller s, err := json.Marshal(&struct{Id string}{Id: "testnetwork"})
// with proper endpoints req, err := http.NewRequest("POST", domain + ":" + port, bytes.NewReader(s))
if err != nil {
t.Fatalf("unexpected error creating request: %v", err)
}
resp, err := (&http.Client{}).Do(req)
if err != nil {
t.Fatalf("unexpected error on http.Client request: %v", err)
}
req, err = http.NewRequest("POST", domain + ":" + port + "/testnetwork/debug/", nil)
if err != nil {
t.Fatalf("unexpected error creating request: %v", err)
}
resp, err = (&http.Client{}).Do(req)
if err != nil {
t.Fatalf("unexpected error on http.Client request: %v", err)
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
t.Fatalf("error reading response body: %v", err)
}
t.Logf("%s", body)
}
func TestNodes(t *testing.T) {
networkname := "testnetworkfornodes"
s, err := json.Marshal(&struct{Id string}{Id: networkname})
req, err := http.NewRequest("POST", domain + ":" + port, bytes.NewReader(s))
if err != nil {
t.Fatalf("unexpected error creating request: %v", err)
}
resp, err := (&http.Client{}).Do(req)
if err != nil {
t.Fatalf("unexpected error on http.Client request: %v", err)
}
for i := 0; i < 3; i++ {
req, err = http.NewRequest("POST", domain + ":" + port + "/" + networkname + "/node/", nil)
if err != nil {
t.Fatalf("unexpected error creating request: %v", err)
}
resp, err = (&http.Client{}).Do(req)
if err != nil {
t.Fatalf("unexpected error on http.Client request: %v", err)
}
t.Logf("%s", resp)
}
/*
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
t.Fatalf("error reading response body: %v", err)
}
t.Logf("%s", body)
*/
} }
func testResponse(t *testing.T, method, addr string, r io.ReadSeeker) []byte { func testResponse(t *testing.T, method, addr string, r io.ReadSeeker) []byte {
@ -104,7 +161,8 @@ func TestUpdate(t *testing.T) {
"group": "nodes" "group": "nodes"
} }
], ],
"remove": [] "remove": [],
"message": []
}` }`
resp := testResponse(t, "GET", url(port, "0"), bytes.NewReader([]byte("{}"))) resp := testResponse(t, "GET", url(port, "0"), bytes.NewReader([]byte("{}")))
if string(resp) != exp { if string(resp) != exp {

View file

@ -11,7 +11,6 @@ import (
"time" "time"
"github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/p2p/adapters" "github.com/ethereum/go-ethereum/p2p/adapters"
) )
@ -36,8 +35,8 @@ type TestNetAdapter interface {
type TestMessenger interface { type TestMessenger interface {
// MsgPipe([]byte, []byte) p2p,MsgPipe // MsgPipe([]byte, []byte) p2p,MsgPipe
ExpectMsg(p2p.MsgReader, uint64, interface{}) error ExpectMsg(uint64, interface{}) error
TriggerMsg(p2p.MsgWriter, uint64, interface{}) error TriggerMsg(uint64, interface{}) error
} }
// exchanges are the basic units of protocol tests // exchanges are the basic units of protocol tests
@ -73,17 +72,18 @@ type Disconnect struct {
// it allows for resource-driven scenario testing // it allows for resource-driven scenario testing
// disconnect reason errors are written in session.Errs // disconnect reason errors are written in session.Errs
// (correcponding to session.Peers) // (correcponding to session.Peers)
func NewExchangeTestSession(t *testing.T, n TestNetAdapter, m TestMessenger, ids []*adapters.NodeId) *ExchangeTestSession { //func NewExchangeTestSession(t *testing.T, n TestNetAdapter, m TestMessenger, ids []*adapters.NodeId) *ExchangeTestSession {
func NewExchangeTestSession(t *testing.T, n TestNetAdapter, ids []*adapters.NodeId) *ExchangeTestSession {
return &ExchangeTestSession{ return &ExchangeTestSession{
Ids: ids, Ids: ids,
TestNetAdapter: n, TestNetAdapter: n,
TestMessenger: m,
t: t, t: t,
} }
} }
type TestPeerInfo struct { type TestPeerInfo struct {
RW p2p.MsgReadWriter //RW p2p.MsgReadWriter
Messenger TestMessenger
Flushc chan bool Flushc chan bool
Errc chan error Errc chan error
} }
@ -94,15 +94,17 @@ func (self *ExchangeTestSession) trigger(trig Trigger) error {
if peer == nil { if peer == nil {
panic(fmt.Sprintf("trigger: peer %v does not exist (1- %v)", trig.Peer, len(self.Ids))) panic(fmt.Sprintf("trigger: peer %v does not exist (1- %v)", trig.Peer, len(self.Ids)))
} }
rw := peer.RW //rw := peer.RW
if rw == nil { m := peer.Messenger
if m == nil {
return fmt.Errorf("trigger: peer %v unreachable", trig.Peer) return fmt.Errorf("trigger: peer %v unreachable", trig.Peer)
} }
errc := make(chan error) errc := make(chan error)
go func() { go func() {
glog.V(6).Infof("trigger....") glog.V(6).Infof("trigger....")
errc <- self.TriggerMsg(rw, trig.Code, trig.Msg) //errc <- self.TriggerMsg(rw, trig.Code, trig.Msg)
errc <- m.(TestMessenger).TriggerMsg(trig.Code, trig.Msg)
glog.V(6).Infof("triggered") glog.V(6).Infof("triggered")
}() }()
@ -132,15 +134,17 @@ func (self *ExchangeTestSession) expect(exp Expect) error {
if peer == nil { if peer == nil {
panic(fmt.Sprintf("expect: peer %v does not exist (1- %v)", exp.Peer, len(self.Ids))) panic(fmt.Sprintf("expect: peer %v does not exist (1- %v)", exp.Peer, len(self.Ids)))
} }
rw := peer.RW //rw := peer.RW
if rw == nil { m := peer.Messenger
if m == nil {
return fmt.Errorf("trigger: peer %v unreachable", exp.Peer) return fmt.Errorf("trigger: peer %v unreachable", exp.Peer)
} }
errc := make(chan error) errc := make(chan error)
go func() { go func() {
glog.V(6).Infof("waiting for msg, %v", exp.Msg) glog.V(6).Infof("waiting for msg, %v", exp.Msg)
errc <- self.ExpectMsg(rw, exp.Code, exp.Msg) //errc <- self.ExpectMsg(rw, exp.Code, exp.Msg)
errc <- m.(TestMessenger).ExpectMsg(exp.Code, exp.Msg)
}() }()
t := exp.Timeout t := exp.Timeout

View file

@ -30,7 +30,7 @@ type ExchangeSession struct {
// correct message exchange, forwarding, and broadcast // correct message exchange, forwarding, and broadcast
// higher level or network behaviour should be tested with network simulators // higher level or network behaviour should be tested with network simulators
func NewProtocolTester(t *testing.T, id *adapters.NodeId, n int, run func(id adapters.NodeAdapter) adapters.ProtoCall) *ExchangeSession { func NewProtocolTester(t *testing.T, id *adapters.NodeId, n int, run func(id adapters.NodeAdapter) adapters.ProtoCall) *ExchangeSession {
simPipe := &adapters.SimPipe{} simPipe := adapters.NewSimPipe
network := simulations.NewNetwork(nil, nil) network := simulations.NewNetwork(nil, nil)
naf := func(conf *simulations.NodeConfig) adapters.NodeAdapter { naf := func(conf *simulations.NodeConfig) adapters.NodeAdapter {
na := adapters.NewSimNode(conf.Id, network, simPipe) na := adapters.NewSimNode(conf.Id, network, simPipe)
@ -49,7 +49,8 @@ func NewProtocolTester(t *testing.T, id *adapters.NodeId, n int, run func(id ada
} }
glog.V(6).Infof("network created") glog.V(6).Infof("network created")
na := network.GetNode(id).Adapter() na := network.GetNode(id).Adapter()
s := NewExchangeTestSession(t, na.(TestNetAdapter), na.Messenger().(TestMessenger), nil) //s := NewExchangeTestSession(t, na.(TestNetAdapter), na.Messenger().(TestMessenger), nil)
s := NewExchangeTestSession(t, na.(TestNetAdapter), nil)
self := &ExchangeSession{ self := &ExchangeSession{
network: network, network: network,
na: na, na: na,

View file

@ -86,9 +86,44 @@ func BzzCodeMap(msgs ...interface{}) *protocols.CodeMap {
return ct return ct
} }
func Bzz(localAddr []byte, hive PeerPool, na adapters.NodeAdapter, ct *protocols.CodeMap, services func(Node) error) *p2p.Protocol {
run := func(p *protocols.Peer) error {
addr := &peerAddr{localAddr, na.LocalAddr()}
bee := &bzz{Peer: p, hive: hive, network: na, localAddr: addr}
// protocol handshake and its validation
// sets remote peer address
err := bee.bzzHandshake()
if err != nil {
glog.V(6).Infof("handshake error in peer %v: %v", bee.ID(), err)
return err
}
// mount external service models on the peer connection (swap, sync)
if services != nil {
err = services(bee)
if err != nil {
glog.V(6).Infof("protocol service error for peer %v: %v", bee.ID(), err)
return err
}
}
err = hive.Add(bee)
if err != nil {
glog.V(6).Infof("failed to add peer '%v' to hive: %v", bee.ID(), err)
return err
}
defer hive.Remove(bee)
return bee.Run()
}
return protocols.NewProtocol(ProtocolName, Version, run, na, ct)
}
// Bzz is the protocol constructor // Bzz is the protocol constructor
// returns p2p.Protocol that is to be offered by the node.Service // returns p2p.Protocol that is to be offered by the node.Service
func Bzz(localAddr []byte, hive PeerPool, na adapters.NodeAdapter, m adapters.Messenger, ct *protocols.CodeMap, services func(Node) error) *p2p.Protocol { /*func Bzz(localAddr []byte, hive PeerPool, na adapters.NodeAdapter, m adapters.Messenger, ct *protocols.CodeMap, services func(Node) error) *p2p.Protocol {
// handle handshake // handle handshake
run := func(p *p2p.Peer, rw p2p.MsgReadWriter) error { run := func(p *p2p.Peer, rw p2p.MsgReadWriter) error {
@ -145,6 +180,8 @@ func Bzz(localAddr []byte, hive PeerPool, na adapters.NodeAdapter, m adapters.Me
} }
} }
*/
/* /*
Handshake Handshake