github.com/core-coin/go-core/v2@v2.1.9/p2p/simulations/examples/ping-pong.go (about)

     1  // Copyright 2017 by the Authors
     2  // This file is part of the go-core library.
     3  //
     4  // The go-core library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-core library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-core library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package main
    18  
    19  import (
    20  	"flag"
    21  	"fmt"
    22  	"io/ioutil"
    23  	"net/http"
    24  	"os"
    25  	"sync/atomic"
    26  	"time"
    27  
    28  	"github.com/core-coin/go-core/v2/log"
    29  	"github.com/core-coin/go-core/v2/node"
    30  	"github.com/core-coin/go-core/v2/p2p"
    31  	"github.com/core-coin/go-core/v2/p2p/enode"
    32  	"github.com/core-coin/go-core/v2/p2p/simulations"
    33  	"github.com/core-coin/go-core/v2/p2p/simulations/adapters"
    34  )
    35  
    36  var adapterType = flag.String("adapter", "sim", `node adapter to use (one of "sim", "exec" or "docker")`)
    37  
    38  // main() starts a simulation network which contains nodes running a simple
    39  // ping-pong protocol
    40  func main() {
    41  	flag.Parse()
    42  
    43  	// set the log level to Trace
    44  	log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(false))))
    45  
    46  	// register a single ping-pong service
    47  	services := map[string]adapters.LifecycleConstructor{
    48  		"ping-pong": func(ctx *adapters.ServiceContext, stack *node.Node) (node.Lifecycle, error) {
    49  			pps := newPingPongService(ctx.Config.ID)
    50  			stack.RegisterProtocols(pps.Protocols())
    51  			return pps, nil
    52  		},
    53  	}
    54  	adapters.RegisterLifecycles(services)
    55  
    56  	// create the NodeAdapter
    57  	var adapter adapters.NodeAdapter
    58  
    59  	switch *adapterType {
    60  
    61  	case "sim":
    62  		log.Info("using sim adapter")
    63  		adapter = adapters.NewSimAdapter(services)
    64  
    65  	case "exec":
    66  		tmpdir, err := ioutil.TempDir("", "p2p-example")
    67  		if err != nil {
    68  			log.Crit("error creating temp dir", "err", err)
    69  		}
    70  		defer os.RemoveAll(tmpdir)
    71  		log.Info("using exec adapter", "tmpdir", tmpdir)
    72  		adapter = adapters.NewExecAdapter(tmpdir)
    73  
    74  	default:
    75  		log.Crit(fmt.Sprintf("unknown node adapter %q", *adapterType))
    76  	}
    77  
    78  	// start the HTTP API
    79  	log.Info("starting simulation server on 0.0.0.0:8888...")
    80  	network := simulations.NewNetwork(adapter, &simulations.NetworkConfig{
    81  		DefaultService: "ping-pong",
    82  	})
    83  	if err := http.ListenAndServe(":8888", simulations.NewServer(network)); err != nil {
    84  		log.Crit("error starting simulation server", "err", err)
    85  	}
    86  }
    87  
    88  // pingPongService runs a ping-pong protocol between nodes where each node
    89  // sends a ping to all its connected peers every 10s and receives a pong in
    90  // return
    91  type pingPongService struct {
    92  	id       enode.ID
    93  	log      log.Logger
    94  	received int64
    95  }
    96  
    97  func newPingPongService(id enode.ID) *pingPongService {
    98  	return &pingPongService{
    99  		id:  id,
   100  		log: log.New("node.id", id),
   101  	}
   102  }
   103  
   104  func (p *pingPongService) Protocols() []p2p.Protocol {
   105  	return []p2p.Protocol{{
   106  		Name:     "ping-pong",
   107  		Version:  1,
   108  		Length:   2,
   109  		Run:      p.Run,
   110  		NodeInfo: p.Info,
   111  	}}
   112  }
   113  
   114  func (p *pingPongService) Start() error {
   115  	p.log.Info("ping-pong service starting")
   116  	return nil
   117  }
   118  
   119  func (p *pingPongService) Stop() error {
   120  	p.log.Info("ping-pong service stopping")
   121  	return nil
   122  }
   123  
   124  func (p *pingPongService) Info() interface{} {
   125  	return struct {
   126  		Received int64 `json:"received"`
   127  	}{
   128  		atomic.LoadInt64(&p.received),
   129  	}
   130  }
   131  
   132  const (
   133  	pingMsgCode = iota
   134  	pongMsgCode
   135  )
   136  
   137  // Run implements the ping-pong protocol which sends ping messages to the peer
   138  // at 10s intervals, and responds to pings with pong messages.
   139  func (p *pingPongService) Run(peer *p2p.Peer, rw p2p.MsgReadWriter) error {
   140  	log := p.log.New("peer.id", peer.ID())
   141  
   142  	errC := make(chan error)
   143  	go func() {
   144  		for range time.Tick(10 * time.Second) {
   145  			log.Info("sending ping")
   146  			if err := p2p.Send(rw, pingMsgCode, "PING"); err != nil {
   147  				errC <- err
   148  				return
   149  			}
   150  		}
   151  	}()
   152  	go func() {
   153  		for {
   154  			msg, err := rw.ReadMsg()
   155  			if err != nil {
   156  				errC <- err
   157  				return
   158  			}
   159  			payload, err := ioutil.ReadAll(msg.Payload)
   160  			if err != nil {
   161  				errC <- err
   162  				return
   163  			}
   164  			log.Info("received message", "msg.code", msg.Code, "msg.payload", string(payload))
   165  			atomic.AddInt64(&p.received, 1)
   166  			if msg.Code == pingMsgCode {
   167  				log.Info("sending pong")
   168  				go p2p.Send(rw, pongMsgCode, "PONG")
   169  			}
   170  		}
   171  	}()
   172  	return <-errC
   173  }