simulations

package
v0.0.0-...-5d85a61 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 12, 2023 License: GPL-3.0 Imports: 21 Imported by: 0

README

devp2p Simulations

The p2p/simulations package implements a simulation framework which supports creating a collection of devp2p nodes, connecting them together to form a simulation network, performing simulation actions in that network and then extracting useful information.

Nodes

Each node in a simulation network runs multiple services by wrapping a collection of objects which implement the node.Service interface meaning they:

  • can be started and stopped
  • run p2p protocols
  • expose RPC APIs

This means that any object which implements the node.Service interface can be used to run a node in the simulation.

Services

Before running a simulation, a set of service initializers must be registered which can then be used to run nodes in the network.

A service initializer is a function with the following signature:

func(ctx *adapters.ServiceContext) (node.Service, error)

These initializers should be registered by calling the adapters.RegisterServices function in an init() hook:

func init() {
	adapters.RegisterServices(adapters.Services{
		"service1": initService1,
		"service2": initService2,
	})
}

Node Adapters

The simulation framework includes multiple "node adapters" which are responsible for creating an environment in which a node runs.

SimAdapter

The SimAdapter runs nodes in-memory, connecting them using an in-memory, synchronous net.Pipe and connecting to their RPC server using an in-memory rpc.Client.

ExecAdapter

The ExecAdapter runs nodes as side processes of the running simulation.

It does this by executing the binary which is running the simulation but setting argv[0] (i.e. the program name) to p2p-node which is then detected by an init hook in the side process which runs the node.Service using the devp2p node stack rather than executing main().

The nodes listen for devp2p connections and WebSocket RPC clients on random localhost ports.

DockerAdapter

The DockerAdapter is similar to the ExecAdapter but executes docker run to run the node in a Docker container using a Docker image containing the simulation binary at /bin/p2p-node.

The Docker image is built using docker build when the adapter is initialised, meaning no prior setup is necessary other than having a working Docker client.

Each node listens on the external IP of the container and the default p2p and RPC ports (30303 and 8546 respectively).

Network

A simulation network is created with an ID and default service (which is used if a node is created without an explicit service), exposes methods for creating, starting, stopping, connecting and disconnecting nodes, and emits events when certain actions occur.

Events

A simulation network emits the following events:

  • node event - when nodes are created / started / stopped
  • connection event - when nodes are connected / disconnected
  • message event - when a protocol message is sent between two nodes

The events have a "control" flag which when set indicates that the event is the outcome of a controlled simulation action (e.g. creating a node or explicitly connecting two nodes together).

This is in contrast to a non-control event, otherwise called a "live" event, which is the outcome of something happening in the network as a result of a control event (e.g. a node actually started up or a connection was actually established between two nodes).

Live events are detected by the simulation network by subscribing to node peer events via RPC when the nodes start up.

Testing Framework

The Simulation type can be used in tests to perform actions in a simulation network and then wait for expectations to be met.

With a running simulation network, the Simulation.Run method can be called with a Step which has the following fields:

  • Action - a function which performs some action in the network

  • Expect - an expectation function which returns whether or not a given node meets the expectation

  • Trigger - a channel which receives node IDs which then trigger a check of the expectation function to be performed against that node

As a concrete example, consider a simulated network of Ethereum nodes. An Action could be the sending of a transaction, Expect it being included in a block, and Trigger a check for every block that is mined.

On return, the Simulation.Run method returns a StepResult which can be used to determine if all nodes met the expectation, how long it took them to meet the expectation and what network events were emitted during the step run.

HTTP API

The simulation framework includes a HTTP API which can be used to control the simulation.

The API is initialised with a particular node adapter and has the following endpoints:

GET    /                            Get network information
POST   /start                       Start all nodes in the network
POST   /stop                        Stop all nodes in the network
GET    /events                      Stream network events
GET    /snapshot                    Take a network snapshot
POST   /snapshot                    Load a network snapshot
POST   /nodes                       Create a node
GET    /nodes                       Get all nodes in the network
GET    /nodes/:nodeid               Get node information
POST   /nodes/:nodeid/start         Start a node
POST   /nodes/:nodeid/stop          Stop a node
POST   /nodes/:nodeid/conn/:peerid  Connect two nodes
DELETE /nodes/:nodeid/conn/:peerid  Disconnect two nodes
GET    /nodes/:nodeid/rpc           Make RPC requests to a node via WebSocket

For convenience, nodeid in the URL can be the name of a node rather than its ID.

Command line client

p2psim is a command line client for the HTTP API, located in cmd/p2psim.

It provides the following commands:

p2psim show
p2psim events [--current] [--filter=FILTER]
p2psim snapshot
p2psim load
p2psim node create [--name=NAME] [--services=SERVICES] [--key=KEY]
p2psim node list
p2psim node show <node>
p2psim node start <node>
p2psim node stop <node>
p2psim node connect <node> <peer>
p2psim node disconnect <node> <peer>
p2psim node rpc <node> <method> [<args>] [--subscribe]

Example

See p2p/simulations/examples/README.md.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var DefaultClient = NewClient("http://localhost:8888")

Functions

func ConnLabel

func ConnLabel(source, target discover.NodeID) string

func GetMockerList

func GetMockerList() []string

func LookupMocker

func LookupMocker(mockerType string) func(net *Network, quit chan struct{}, nodeCount int)

Types

type Client

type Client struct {
	URL string
	// contains filtered or unexported fields
}

func NewClient

func NewClient(url string) *Client

func (*Client) ConnectNode

func (c *Client) ConnectNode(nodeID, peerID string) error

func (*Client) CreateNode

func (c *Client) CreateNode(config *adapters.NodeConfig) (*p2p.NodeInfo, error)

func (*Client) CreateSnapshot

func (c *Client) CreateSnapshot() (*Snapshot, error)

func (*Client) Delete

func (c *Client) Delete(path string) error

func (*Client) DisconnectNode

func (c *Client) DisconnectNode(nodeID, peerID string) error

func (*Client) Get

func (c *Client) Get(path string, out interface{}) error

func (*Client) GetNetwork

func (c *Client) GetNetwork() (*Network, error)

func (*Client) GetNode

func (c *Client) GetNode(nodeID string) (*p2p.NodeInfo, error)

func (*Client) GetNodes

func (c *Client) GetNodes() ([]*p2p.NodeInfo, error)

func (*Client) LoadSnapshot

func (c *Client) LoadSnapshot(snap *Snapshot) error

func (*Client) Post

func (c *Client) Post(path string, in, out interface{}) error

func (*Client) RPCClient

func (c *Client) RPCClient(ctx context.Context, nodeID string) (*rpc.Client, error)

func (*Client) Send

func (c *Client) Send(method, path string, in, out interface{}) error

func (*Client) StartNetwork

func (c *Client) StartNetwork() error

func (*Client) StartNode

func (c *Client) StartNode(nodeID string) error

func (*Client) StopNetwork

func (c *Client) StopNetwork() error

func (*Client) StopNode

func (c *Client) StopNode(nodeID string) error

func (*Client) SubscribeNetwork

func (c *Client) SubscribeNetwork(events chan *Event, opts SubscribeOpts) (event.Subscription, error)

type Conn

type Conn struct {
	One discover.NodeID `json:"one"`

	Other discover.NodeID `json:"other"`

	Up bool `json:"up"`
	// contains filtered or unexported fields
}

func (*Conn) String

func (self *Conn) String() string

type Event

type Event struct {
	Type EventType `json:"type"`

	Time time.Time `json:"time"`

	Control bool `json:"control"`

	Node *Node `json:"node,omitempty"`

	Conn *Conn `json:"conn,omitempty"`

	Msg *Msg `json:"msg,omitempty"`
}

func ControlEvent

func ControlEvent(v interface{}) *Event

func NewEvent

func NewEvent(v interface{}) *Event

func (*Event) String

func (e *Event) String() string

type EventType

type EventType string
const (
	EventTypeNode EventType = "node"

	EventTypeConn EventType = "conn"

	EventTypeMsg EventType = "msg"
)

type Expectation

type Expectation struct {
	Nodes []discover.NodeID

	Check func(context.Context, discover.NodeID) (bool, error)
}

type Msg

type Msg struct {
	One      discover.NodeID `json:"one"`
	Other    discover.NodeID `json:"other"`
	Protocol string          `json:"protocol"`
	Code     uint64          `json:"code"`
	Received bool            `json:"received"`
}

func (*Msg) String

func (self *Msg) String() string

type MsgFilter

type MsgFilter struct {
	Proto string

	Code int64
}

type MsgFilters

type MsgFilters map[MsgFilter]struct{}

func NewMsgFilters

func NewMsgFilters(filterParam string) (MsgFilters, error)

func (MsgFilters) Match

func (m MsgFilters) Match(msg *Msg) bool

type Network

type Network struct {
	NetworkConfig

	Nodes []*Node `json:"nodes"`

	Conns []*Conn `json:"conns"`
	// contains filtered or unexported fields
}

func NewNetwork

func NewNetwork(nodeAdapter adapters.NodeAdapter, conf *NetworkConfig) *Network

func (*Network) Config

func (self *Network) Config() *NetworkConfig

func (*Network) Connect

func (self *Network) Connect(oneID, otherID discover.NodeID) error

func (*Network) DidConnect

func (self *Network) DidConnect(one, other discover.NodeID) error

func (*Network) DidDisconnect

func (self *Network) DidDisconnect(one, other discover.NodeID) error

func (*Network) DidReceive

func (self *Network) DidReceive(sender, receiver discover.NodeID, proto string, code uint64) error

func (*Network) DidSend

func (self *Network) DidSend(sender, receiver discover.NodeID, proto string, code uint64) error

func (*Network) Disconnect

func (self *Network) Disconnect(oneID, otherID discover.NodeID) error

func (*Network) Events

func (self *Network) Events() *event.Feed

func (*Network) GetConn

func (self *Network) GetConn(oneID, otherID discover.NodeID) *Conn

func (*Network) GetNode

func (self *Network) GetNode(id discover.NodeID) *Node

func (*Network) GetNodeByName

func (self *Network) GetNodeByName(name string) *Node

func (*Network) GetNodes

func (self *Network) GetNodes() (nodes []*Node)

func (*Network) GetOrCreateConn

func (self *Network) GetOrCreateConn(oneID, otherID discover.NodeID) (*Conn, error)

func (*Network) InitConn

func (self *Network) InitConn(oneID, otherID discover.NodeID) (*Conn, error)

func (*Network) Load

func (self *Network) Load(snap *Snapshot) error

func (*Network) NewNode

func (self *Network) NewNode() (*Node, error)

func (*Network) NewNodeWithConfig

func (self *Network) NewNodeWithConfig(conf *adapters.NodeConfig) (*Node, error)

func (*Network) Reset

func (self *Network) Reset()

func (*Network) Shutdown

func (self *Network) Shutdown()

func (*Network) Snapshot

func (self *Network) Snapshot() (*Snapshot, error)

func (*Network) Start

func (self *Network) Start(id discover.NodeID) error

func (*Network) StartAll

func (self *Network) StartAll() error

func (*Network) Stop

func (self *Network) Stop(id discover.NodeID) error

func (*Network) StopAll

func (self *Network) StopAll() error

func (*Network) Subscribe

func (self *Network) Subscribe(events chan *Event)

type NetworkConfig

type NetworkConfig struct {
	ID             string `json:"id"`
	DefaultService string `json:"default_service,omitempty"`
}

type Node

type Node struct {
	adapters.Node `json:"-"`

	Config *adapters.NodeConfig `json:"config"`

	Up bool `json:"up"`
}

func (*Node) ID

func (self *Node) ID() discover.NodeID

func (*Node) MarshalJSON

func (self *Node) MarshalJSON() ([]byte, error)

func (*Node) NodeInfo

func (self *Node) NodeInfo() *p2p.NodeInfo

func (*Node) String

func (self *Node) String() string

type NodeSnapshot

type NodeSnapshot struct {
	Node Node `json:"node,omitempty"`

	Snapshots map[string][]byte `json:"snapshots,omitempty"`
}

type Server

type Server struct {
	// contains filtered or unexported fields
}

func NewServer

func NewServer(network *Network) *Server

func (*Server) ConnectNode

func (s *Server) ConnectNode(w http.ResponseWriter, req *http.Request)

func (*Server) CreateNode

func (s *Server) CreateNode(w http.ResponseWriter, req *http.Request)

func (*Server) CreateSnapshot

func (s *Server) CreateSnapshot(w http.ResponseWriter, req *http.Request)

func (*Server) DELETE

func (s *Server) DELETE(path string, handle http.HandlerFunc)

func (*Server) DisconnectNode

func (s *Server) DisconnectNode(w http.ResponseWriter, req *http.Request)

func (*Server) GET

func (s *Server) GET(path string, handle http.HandlerFunc)

func (*Server) GetMockers

func (s *Server) GetMockers(w http.ResponseWriter, req *http.Request)

func (*Server) GetNetwork

func (s *Server) GetNetwork(w http.ResponseWriter, req *http.Request)

func (*Server) GetNode

func (s *Server) GetNode(w http.ResponseWriter, req *http.Request)

func (*Server) GetNodes

func (s *Server) GetNodes(w http.ResponseWriter, req *http.Request)

func (*Server) JSON

func (s *Server) JSON(w http.ResponseWriter, status int, data interface{})

func (*Server) LoadSnapshot

func (s *Server) LoadSnapshot(w http.ResponseWriter, req *http.Request)

func (*Server) NodeRPC

func (s *Server) NodeRPC(w http.ResponseWriter, req *http.Request)

func (*Server) OPTIONS

func (s *Server) OPTIONS(path string, handle http.HandlerFunc)

func (*Server) Options

func (s *Server) Options(w http.ResponseWriter, req *http.Request)

func (*Server) POST

func (s *Server) POST(path string, handle http.HandlerFunc)

func (*Server) ResetNetwork

func (s *Server) ResetNetwork(w http.ResponseWriter, req *http.Request)

func (*Server) ServeHTTP

func (s *Server) ServeHTTP(w http.ResponseWriter, req *http.Request)

func (*Server) StartMocker

func (s *Server) StartMocker(w http.ResponseWriter, req *http.Request)

func (*Server) StartNetwork

func (s *Server) StartNetwork(w http.ResponseWriter, req *http.Request)

func (*Server) StartNode

func (s *Server) StartNode(w http.ResponseWriter, req *http.Request)

func (*Server) StopMocker

func (s *Server) StopMocker(w http.ResponseWriter, req *http.Request)

func (*Server) StopNetwork

func (s *Server) StopNetwork(w http.ResponseWriter, req *http.Request)

func (*Server) StopNode

func (s *Server) StopNode(w http.ResponseWriter, req *http.Request)

func (*Server) StreamNetworkEvents

func (s *Server) StreamNetworkEvents(w http.ResponseWriter, req *http.Request)

type Simulation

type Simulation struct {
	// contains filtered or unexported fields
}

func NewSimulation

func NewSimulation(network *Network) *Simulation

func (*Simulation) Run

func (s *Simulation) Run(ctx context.Context, step *Step) (result *StepResult)

type Snapshot

type Snapshot struct {
	Nodes []NodeSnapshot `json:"nodes,omitempty"`
	Conns []Conn         `json:"conns,omitempty"`
}

type Step

type Step struct {
	Action func(context.Context) error

	Trigger chan discover.NodeID

	Expect *Expectation
}

type StepResult

type StepResult struct {
	Error error

	StartedAt time.Time

	FinishedAt time.Time

	Passes map[discover.NodeID]time.Time

	NetworkEvents []*Event
}

type SubscribeOpts

type SubscribeOpts struct {
	Current bool

	Filter string
}

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL