github.com/annchain/OG@v0.0.9/plugin/community/manager.go (about)

     1  package community
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"github.com/annchain/OG/ffchan"
     8  	"github.com/libp2p/go-libp2p"
     9  	core "github.com/libp2p/go-libp2p-core/crypto"
    10  	"github.com/libp2p/go-libp2p-core/host"
    11  	"github.com/libp2p/go-libp2p-core/network"
    12  	"github.com/libp2p/go-libp2p-core/peer"
    13  	"github.com/libp2p/go-libp2p-core/peerstore"
    14  	swarm "github.com/libp2p/go-libp2p-swarm"
    15  	"github.com/multiformats/go-multiaddr"
    16  	"github.com/sirupsen/logrus"
    17  	"github.com/tinylib/msgp/msgp"
    18  	"log"
    19  	"math/rand"
    20  	"sync"
    21  	"time"
    22  )
    23  
    24  var BackoffConnect = time.Second * 5
    25  var IpLayerProtocol = "tcp"
    26  
    27  type IoEvent struct {
    28  	Neighbour *Neighbour
    29  	Err       error
    30  }
    31  
    32  type Neighbour struct {
    33  	Id              peer.ID
    34  	Stream          network.Stream
    35  	IoEventChannel  chan *IoEvent
    36  	IncomingChannel chan *WireMessage
    37  	msgpReader      *msgp.Reader
    38  	msgpWriter      *msgp.Writer
    39  	event           chan bool
    40  	outgoingChannel chan *Msg
    41  	quit            chan bool
    42  }
    43  
    44  func (c *Neighbour) InitDefault() {
    45  	c.event = make(chan bool)
    46  	c.quit = make(chan bool)
    47  	c.outgoingChannel = make(chan *Msg) // messages already dispatched
    48  }
    49  
    50  func (c *Neighbour) StartRead() {
    51  	var err error
    52  	c.msgpReader = msgp.NewReader(c.Stream)
    53  	for {
    54  		msg := &WireMessage{}
    55  		err = msg.DecodeMsg(c.msgpReader)
    56  		if err != nil {
    57  			// bad message, drop
    58  			logrus.WithError(err).Warn("read error")
    59  			break
    60  		}
    61  
    62  		<-ffchan.NewTimeoutSenderShort(c.IncomingChannel, msg, "read").C
    63  		//c.IncomingChannel <- msg
    64  	}
    65  	// neighbour disconnected, notify the communicator
    66  	c.IoEventChannel <- &IoEvent{
    67  		Neighbour: c,
    68  		Err:       err,
    69  	}
    70  
    71  }
    72  
    73  func (c *Neighbour) StartWrite() {
    74  	var err error
    75  	c.msgpWriter = msgp.NewWriter(c.Stream)
    76  loop:
    77  	for {
    78  		select {
    79  		case req := <-c.outgoingChannel:
    80  			logrus.Trace("neighbour got send request")
    81  			contentBytes, err := req.Content.MarshalMsg([]byte{})
    82  			if err != nil {
    83  				panic(err)
    84  			}
    85  
    86  			wireMessage := WireMessage{
    87  				MsgType:      int(req.Typev),
    88  				ContentBytes: contentBytes,
    89  				SenderId:     req.SenderId,
    90  				Signature:    req.Sig,
    91  			}
    92  
    93  			err = wireMessage.EncodeMsg(c.msgpWriter)
    94  			if err != nil {
    95  				break
    96  			}
    97  			err = c.msgpWriter.Flush()
    98  			if err != nil {
    99  				break
   100  			}
   101  			logrus.Trace("neighbour sent")
   102  
   103  		case <-c.quit:
   104  			break loop
   105  		}
   106  	}
   107  	// neighbour disconnected, notify the communicator
   108  	c.IoEventChannel <- &IoEvent{
   109  		Neighbour: c,
   110  		Err:       err,
   111  	}
   112  }
   113  
   114  func (c *Neighbour) Send(req *Msg) {
   115  	<-ffchan.NewTimeoutSenderShort(c.outgoingChannel, req, "send").C
   116  	//c.outgoingChannel <- req
   117  }
   118  
   119  // LibP2pPhysicalCommunicator
   120  type LibP2pPhysicalCommunicator struct {
   121  	Port       int // listening port
   122  	PrivateKey core.PrivKey
   123  
   124  	node            host.Host              // p2p host to receive new streams
   125  	activePeers     map[peer.ID]*Neighbour // active peers that will be reconnect if error
   126  	tryingPeers     map[peer.ID]bool       // peers that is trying to connect.
   127  	incomingChannel chan *WireMessage      // incoming message channel
   128  	outgoingChannel chan *OutgoingRequest  // universal outgoing channel to collect send requests
   129  	ioEventChannel  chan *IoEvent          // receive event when peer disconnects
   130  
   131  	initWait sync.WaitGroup
   132  	quit     chan bool
   133  	mu       sync.RWMutex
   134  }
   135  
   136  func (c *LibP2pPhysicalCommunicator) InitDefault() {
   137  	c.activePeers = make(map[peer.ID]*Neighbour)
   138  	c.tryingPeers = make(map[peer.ID]bool)
   139  	c.outgoingChannel = make(chan *OutgoingRequest)
   140  	c.incomingChannel = make(chan *WireMessage)
   141  	c.ioEventChannel = make(chan *IoEvent)
   142  	c.initWait.Add(1)
   143  	c.quit = make(chan bool)
   144  }
   145  
   146  func (c *LibP2pPhysicalCommunicator) Start() {
   147  	// start consuming queue
   148  	go c.Listen()
   149  	go c.consumeQueue()
   150  	go c.recoverPeer()
   151  }
   152  
   153  func (c *LibP2pPhysicalCommunicator) consumeQueue() {
   154  	c.initWait.Wait()
   155  	for {
   156  		select {
   157  		case req := <-c.outgoingChannel:
   158  			logrus.WithField("req", req).Trace("physical communicator got a request from outgoing channel")
   159  			go c.handleRequest(req)
   160  		case <-c.quit:
   161  			return
   162  		}
   163  	}
   164  
   165  }
   166  
   167  func (c *LibP2pPhysicalCommunicator) Stop() {
   168  	close(c.quit)
   169  	// shut the node down
   170  	if err := c.node.Close(); err != nil {
   171  		panic(err)
   172  	}
   173  }
   174  
   175  func (c *LibP2pPhysicalCommunicator) GetIncomingChannel() chan *WireMessage {
   176  	return c.incomingChannel
   177  }
   178  
   179  func (c *LibP2pPhysicalCommunicator) makeHost(priv core.PrivKey) (host.Host, error) {
   180  	opts := []libp2p.Option{
   181  		libp2p.ListenAddrStrings(fmt.Sprintf("/ip4/127.0.0.1/%s/%d", IpLayerProtocol, c.Port)),
   182  		libp2p.Identity(priv),
   183  		libp2p.DisableRelay(),
   184  	}
   185  	basicHost, err := libp2p.New(context.Background(), opts...)
   186  	if err != nil {
   187  		return nil, err
   188  	}
   189  	return basicHost, nil
   190  }
   191  
   192  func (c *LibP2pPhysicalCommunicator) printHostInfo(basicHost host.Host) {
   193  
   194  	// print the node's listening addresses
   195  	// protocol is always p2p
   196  	hostAddr, err := multiaddr.NewMultiaddr(fmt.Sprintf("/p2p/%s", basicHost.ID().Pretty()))
   197  	if err != nil {
   198  		panic(err)
   199  	}
   200  
   201  	addr := basicHost.Addrs()[0]
   202  	fullAddr := addr.Encapsulate(hostAddr)
   203  	log.Printf("I am %s\n", fullAddr)
   204  }
   205  
   206  func (c *LibP2pPhysicalCommunicator) Listen() {
   207  	// start a libp2p node with default settings
   208  	host, err := c.makeHost(c.PrivateKey)
   209  	if err != nil {
   210  		panic(err)
   211  	}
   212  	c.node = host
   213  
   214  	c.printHostInfo(c.node)
   215  
   216  	c.node.SetStreamHandler(ProtocolId, c.HandlePeerStream)
   217  	c.initWait.Done()
   218  	logrus.Info("waiting for connection...")
   219  	select {
   220  	case <-c.quit:
   221  		err := c.node.Close()
   222  		if err != nil {
   223  			logrus.WithError(err).Warn("closing communicator")
   224  		}
   225  	}
   226  }
   227  
   228  func (c *LibP2pPhysicalCommunicator) HandlePeerStream(s network.Stream) {
   229  	// must lock to prevent double channel
   230  	c.mu.Lock()
   231  	defer c.mu.Unlock()
   232  
   233  	logrus.WithFields(logrus.Fields{
   234  		"peerId":  s.Conn().RemotePeer().String(),
   235  		"address": s.Conn().RemoteMultiaddr().String(),
   236  	}).Info("Peer connection established")
   237  	peerId := s.Conn().RemotePeer()
   238  
   239  	// deregister in the trying list
   240  	delete(c.tryingPeers, s.Conn().RemotePeer())
   241  
   242  	// prevent double channel
   243  	if _, ok := c.activePeers[peerId]; ok {
   244  		// already established. close
   245  		err := s.Close()
   246  		if err != nil {
   247  			logrus.WithError(err).Warn("closing peer")
   248  		}
   249  		return
   250  	}
   251  
   252  	neightbour := &Neighbour{
   253  		Id:              peerId,
   254  		Stream:          s,
   255  		IoEventChannel:  c.ioEventChannel,
   256  		IncomingChannel: c.incomingChannel,
   257  	}
   258  	neightbour.InitDefault()
   259  	c.activePeers[peerId] = neightbour
   260  
   261  	go neightbour.StartRead()
   262  	go neightbour.StartWrite()
   263  }
   264  
   265  func (c *LibP2pPhysicalCommunicator) ClosePeer(id string) {
   266  
   267  }
   268  
   269  func (c *LibP2pPhysicalCommunicator) GetNeighbour(id string) (neighbour *Neighbour, err error) {
   270  	idp, err := peer.Decode(id)
   271  	if err != nil {
   272  		return
   273  	}
   274  	neighbour, ok := c.activePeers[idp]
   275  	if !ok {
   276  		err = errors.New("peer not active")
   277  	}
   278  	return
   279  }
   280  
   281  // SuggestConnection takes a peer address and try to connect to it.
   282  func (c *LibP2pPhysicalCommunicator) SuggestConnection(address string) (peerIds string) {
   283  	c.initWait.Wait()
   284  	logrus.WithField("address", address).Info("registering address")
   285  	fullAddr, err := multiaddr.NewMultiaddr(address)
   286  	if err != nil {
   287  		logrus.WithField("address", address).WithError(err).Warn("bad address")
   288  		return
   289  	}
   290  
   291  	// p2p layer address
   292  	p2pAddr, err := fullAddr.ValueForProtocol(multiaddr.P_P2P)
   293  
   294  	if err != nil {
   295  		logrus.WithField("address", address).WithError(err).Warn("bad address")
   296  	}
   297  
   298  	protocolAddr, err := multiaddr.NewMultiaddr(fmt.Sprintf("/p2p/%s", p2pAddr))
   299  
   300  	if err != nil {
   301  		logrus.WithField("address", address).WithError(err).Warn("bad address")
   302  	}
   303  	// keep only the connection info, wipe out the p2p layer
   304  	connectionAddr := fullAddr.Decapsulate(protocolAddr)
   305  	//fmt.Println("connectionAddr:" + connectionAddr.String())
   306  
   307  	// recover peerId from Base58 Encoded p2pAddr
   308  	peerId, err := peer.Decode(p2pAddr)
   309  	if err != nil {
   310  		logrus.WithField("address", address).WithError(err).Warn("bad address")
   311  	}
   312  	peerIds = peerId.String()
   313  
   314  	//fmt.Println("peerId:" + p2pAddr)
   315  	// check if it is a self connection.
   316  	if peerId == c.node.ID() {
   317  		return
   318  	}
   319  
   320  	// save address and peer info
   321  	c.node.Peerstore().AddAddr(peerId, connectionAddr, peerstore.PermanentAddrTTL)
   322  
   323  	// reg in the trying list
   324  	c.mu.Lock()
   325  	defer c.mu.Unlock()
   326  	if _, ok := c.tryingPeers[peerId]; ok {
   327  		return
   328  	}
   329  	c.tryingPeers[peerId] = true
   330  	return
   331  }
   332  
   333  func (c *LibP2pPhysicalCommunicator) Enqueue(req *OutgoingRequest) {
   334  	logrus.WithField("req", req).Info("Sending message")
   335  	c.initWait.Wait()
   336  	<-ffchan.NewTimeoutSenderShort(c.outgoingChannel, req, "enqueue").C
   337  	//c.outgoingChannel <- req
   338  }
   339  
   340  // we use direct connection currently so let's build a connection if not exists.
   341  func (c *LibP2pPhysicalCommunicator) handleRequest(req *OutgoingRequest) {
   342  	logrus.Trace("handling send request")
   343  	if req.SendType == SendTypeBroadcast {
   344  		for _, neighbour := range c.activePeers {
   345  			go neighbour.Send(req.Msg)
   346  		}
   347  		return
   348  	}
   349  	// find neighbour first
   350  	for _, peerIdEncoded := range req.EndReceivers {
   351  		peerId, err := peer.Decode(peerIdEncoded)
   352  		if err != nil {
   353  			logrus.WithError(err).WithField("peerIdEncoded", peerIdEncoded).Warn("decoding peer")
   354  		}
   355  		logrus.WithField("peerId", peerId).Trace("handling sending requets")
   356  		// get active neighbour
   357  		neighbour, ok := c.activePeers[peerId]
   358  		if !ok {
   359  			// wait for node to be connected. currently node address are pre-located and connections are built ahead.
   360  			logrus.WithField("peerId", peerId).Trace("connection not in active peers")
   361  			continue
   362  		}
   363  		logrus.WithField("peerId", peerId).Trace("go send")
   364  		go neighbour.Send(req.Msg)
   365  	}
   366  }
   367  
   368  func (c *LibP2pPhysicalCommunicator) pickOneAndConnect() {
   369  	c.mu.RLock()
   370  
   371  	if len(c.tryingPeers) == 0 {
   372  		c.mu.RUnlock()
   373  		return
   374  	}
   375  	peerIds := []peer.ID{}
   376  	for k, _ := range c.tryingPeers {
   377  		peerIds = append(peerIds, k)
   378  	}
   379  	c.mu.RUnlock()
   380  
   381  	peerId := peerIds[rand.Intn(len(peerIds))]
   382  
   383  	// start a stream
   384  	logrus.WithField("peerId", peerId).Trace("connecting peer")
   385  	s, err := c.node.NewStream(context.Background(), peerId, ProtocolId)
   386  	if err != nil {
   387  		if err != swarm.ErrDialBackoff {
   388  			//logrus.WithField("stream", s).WithError(err).Warn("error on starting stream")
   389  		}
   390  		return
   391  	}
   392  	// stream built
   393  	// release the lock
   394  	c.HandlePeerStream(s)
   395  
   396  	//hub.handleStream(s)
   397  	//// Create a buffered stream so that read and writes are non blocking.
   398  	//rw := bufio.NewReadWriter(bufio.NewReader(s), bufio.NewWriter(s))
   399  	//
   400  	//// Create a thread to read and write data.
   401  	//go hub.writeData(rw)
   402  	//go hub.readData(rw)
   403  	//logrus.WithField("s", info).WithError(err).Warn("connection established")
   404  }
   405  
   406  func (c *LibP2pPhysicalCommunicator) recoverPeer() {
   407  	for {
   408  		select {
   409  		case <-c.quit:
   410  			return
   411  		case event := <-c.ioEventChannel:
   412  			c.reportPeerDown(event.Neighbour)
   413  		default:
   414  			time.Sleep(time.Second * 1) // at least sleep one second to prevent flood
   415  			c.pickOneAndConnect()
   416  		}
   417  	}
   418  }
   419  
   420  func (c *LibP2pPhysicalCommunicator) reportPeerDown(neighbour *Neighbour) {
   421  	c.mu.Lock()
   422  	defer c.mu.Unlock()
   423  	delete(c.activePeers, neighbour.Id)
   424  	c.tryingPeers[neighbour.Id] = true
   425  }