github.com/luckypickle/go-ethereum-vet@v1.14.2/p2p/simulations/examples/ping-pong.go (about)

     1  // Copyright 2017 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum 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-ethereum 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-ethereum 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/luckypickle/go-ethereum-vet/log"
    29  	"github.com/luckypickle/go-ethereum-vet/node"
    30  	"github.com/luckypickle/go-ethereum-vet/p2p"
    31  	"github.com/luckypickle/go-ethereum-vet/p2p/discover"
    32  	"github.com/luckypickle/go-ethereum-vet/p2p/simulations"
    33  	"github.com/luckypickle/go-ethereum-vet/p2p/simulations/adapters"
    34  	"github.com/luckypickle/go-ethereum-vet/rpc"
    35  )
    36  
    37  var adapterType = flag.String("adapter", "sim", `node adapter to use (one of "sim", "exec" or "docker")`)
    38  
    39  // main() starts a simulation network which contains nodes running a simple
    40  // ping-pong protocol
    41  func main() {
    42  	flag.Parse()
    43  
    44  	// set the log level to Trace
    45  	log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(false))))
    46  
    47  	// register a single ping-pong service
    48  	services := map[string]adapters.ServiceFunc{
    49  		"ping-pong": func(ctx *adapters.ServiceContext) (node.Service, error) {
    50  			return newPingPongService(ctx.Config.ID), nil
    51  		},
    52  	}
    53  	adapters.RegisterServices(services)
    54  
    55  	// create the NodeAdapter
    56  	var adapter adapters.NodeAdapter
    57  
    58  	switch *adapterType {
    59  
    60  	case "sim":
    61  		log.Info("using sim adapter")
    62  		adapter = adapters.NewSimAdapter(services)
    63  
    64  	case "exec":
    65  		tmpdir, err := ioutil.TempDir("", "p2p-example")
    66  		if err != nil {
    67  			log.Crit("error creating temp dir", "err", err)
    68  		}
    69  		defer os.RemoveAll(tmpdir)
    70  		log.Info("using exec adapter", "tmpdir", tmpdir)
    71  		adapter = adapters.NewExecAdapter(tmpdir)
    72  
    73  	case "docker":
    74  		log.Info("using docker adapter")
    75  		var err error
    76  		adapter, err = adapters.NewDockerAdapter()
    77  		if err != nil {
    78  			log.Crit("error creating docker adapter", "err", err)
    79  		}
    80  
    81  	default:
    82  		log.Crit(fmt.Sprintf("unknown node adapter %q", *adapterType))
    83  	}
    84  
    85  	// start the HTTP API
    86  	log.Info("starting simulation server on 0.0.0.0:8888...")
    87  	network := simulations.NewNetwork(adapter, &simulations.NetworkConfig{
    88  		DefaultService: "ping-pong",
    89  	})
    90  	if err := http.ListenAndServe(":8888", simulations.NewServer(network)); err != nil {
    91  		log.Crit("error starting simulation server", "err", err)
    92  	}
    93  }
    94  
    95  // pingPongService runs a ping-pong protocol between nodes where each node
    96  // sends a ping to all its connected peers every 10s and receives a pong in
    97  // return
    98  type pingPongService struct {
    99  	id       discover.NodeID
   100  	log      log.Logger
   101  	received int64
   102  }
   103  
   104  func newPingPongService(id discover.NodeID) *pingPongService {
   105  	return &pingPongService{
   106  		id:  id,
   107  		log: log.New("node.id", id),
   108  	}
   109  }
   110  
   111  func (p *pingPongService) Protocols() []p2p.Protocol {
   112  	return []p2p.Protocol{{
   113  		Name:     "ping-pong",
   114  		Version:  1,
   115  		Length:   2,
   116  		Run:      p.Run,
   117  		NodeInfo: p.Info,
   118  	}}
   119  }
   120  
   121  func (p *pingPongService) APIs() []rpc.API {
   122  	return nil
   123  }
   124  
   125  func (p *pingPongService) Start(server *p2p.Server) error {
   126  	p.log.Info("ping-pong service starting")
   127  	return nil
   128  }
   129  
   130  func (p *pingPongService) Stop() error {
   131  	p.log.Info("ping-pong service stopping")
   132  	return nil
   133  }
   134  
   135  func (p *pingPongService) Info() interface{} {
   136  	return struct {
   137  		Received int64 `json:"received"`
   138  	}{
   139  		atomic.LoadInt64(&p.received),
   140  	}
   141  }
   142  
   143  const (
   144  	pingMsgCode = iota
   145  	pongMsgCode
   146  )
   147  
   148  // Run implements the ping-pong protocol which sends ping messages to the peer
   149  // at 10s intervals, and responds to pings with pong messages.
   150  func (p *pingPongService) Run(peer *p2p.Peer, rw p2p.MsgReadWriter) error {
   151  	log := p.log.New("peer.id", peer.ID())
   152  
   153  	errC := make(chan error)
   154  	go func() {
   155  		for range time.Tick(10 * time.Second) {
   156  			log.Info("sending ping")
   157  			if err := p2p.Send(rw, pingMsgCode, "PING"); err != nil {
   158  				errC <- err
   159  				return
   160  			}
   161  		}
   162  	}()
   163  	go func() {
   164  		for {
   165  			msg, err := rw.ReadMsg()
   166  			if err != nil {
   167  				errC <- err
   168  				return
   169  			}
   170  			payload, err := ioutil.ReadAll(msg.Payload)
   171  			if err != nil {
   172  				errC <- err
   173  				return
   174  			}
   175  			log.Info("received message", "msg.code", msg.Code, "msg.payload", string(payload))
   176  			atomic.AddInt64(&p.received, 1)
   177  			if msg.Code == pingMsgCode {
   178  				log.Info("sending pong")
   179  				go p2p.Send(rw, pongMsgCode, "PONG")
   180  			}
   181  		}
   182  	}()
   183  	return <-errC
   184  }