github.com/klaytn/klaytn@v1.12.1/networks/p2p/simulations/examples/ping-pong.go (about)

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