github.com/cryptotooltop/go-ethereum@v0.0.0-20231103184714-151d1922f3e5/p2p/simulations/adapters/types.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 adapters
    18  
    19  import (
    20  	"crypto/ecdsa"
    21  	"encoding/hex"
    22  	"encoding/json"
    23  	"fmt"
    24  	"net"
    25  	"os"
    26  	"strconv"
    27  
    28  	"github.com/docker/docker/pkg/reexec"
    29  	"github.com/gorilla/websocket"
    30  
    31  	"github.com/scroll-tech/go-ethereum/crypto"
    32  	"github.com/scroll-tech/go-ethereum/log"
    33  	"github.com/scroll-tech/go-ethereum/node"
    34  	"github.com/scroll-tech/go-ethereum/p2p"
    35  	"github.com/scroll-tech/go-ethereum/p2p/enode"
    36  	"github.com/scroll-tech/go-ethereum/p2p/enr"
    37  	"github.com/scroll-tech/go-ethereum/rpc"
    38  )
    39  
    40  // Node represents a node in a simulation network which is created by a
    41  // NodeAdapter, for example:
    42  //
    43  // * SimNode    - An in-memory node
    44  // * ExecNode   - A child process node
    45  // * DockerNode - A Docker container node
    46  //
    47  type Node interface {
    48  	// Addr returns the node's address (e.g. an Enode URL)
    49  	Addr() []byte
    50  
    51  	// Client returns the RPC client which is created once the node is
    52  	// up and running
    53  	Client() (*rpc.Client, error)
    54  
    55  	// ServeRPC serves RPC requests over the given connection
    56  	ServeRPC(*websocket.Conn) error
    57  
    58  	// Start starts the node with the given snapshots
    59  	Start(snapshots map[string][]byte) error
    60  
    61  	// Stop stops the node
    62  	Stop() error
    63  
    64  	// NodeInfo returns information about the node
    65  	NodeInfo() *p2p.NodeInfo
    66  
    67  	// Snapshots creates snapshots of the running services
    68  	Snapshots() (map[string][]byte, error)
    69  }
    70  
    71  // NodeAdapter is used to create Nodes in a simulation network
    72  type NodeAdapter interface {
    73  	// Name returns the name of the adapter for logging purposes
    74  	Name() string
    75  
    76  	// NewNode creates a new node with the given configuration
    77  	NewNode(config *NodeConfig) (Node, error)
    78  }
    79  
    80  // NodeConfig is the configuration used to start a node in a simulation
    81  // network
    82  type NodeConfig struct {
    83  	// ID is the node's ID which is used to identify the node in the
    84  	// simulation network
    85  	ID enode.ID
    86  
    87  	// PrivateKey is the node's private key which is used by the devp2p
    88  	// stack to encrypt communications
    89  	PrivateKey *ecdsa.PrivateKey
    90  
    91  	// Enable peer events for Msgs
    92  	EnableMsgEvents bool
    93  
    94  	// Name is a human friendly name for the node like "node01"
    95  	Name string
    96  
    97  	// Use an existing database instead of a temporary one if non-empty
    98  	DataDir string
    99  
   100  	// Lifecycles are the names of the service lifecycles which should be run when
   101  	// starting the node (for SimNodes it should be the names of service lifecycles
   102  	// contained in SimAdapter.lifecycles, for other nodes it should be
   103  	// service lifecycles registered by calling the RegisterLifecycle function)
   104  	Lifecycles []string
   105  
   106  	// Properties are the names of the properties this node should hold
   107  	// within running services (e.g. "bootnode", "lightnode" or any custom values)
   108  	// These values need to be checked and acted upon by node Services
   109  	Properties []string
   110  
   111  	// ExternalSigner specifies an external URI for a clef-type signer
   112  	ExternalSigner string
   113  
   114  	// Enode
   115  	node *enode.Node
   116  
   117  	// ENR Record with entries to overwrite
   118  	Record enr.Record
   119  
   120  	// function to sanction or prevent suggesting a peer
   121  	Reachable func(id enode.ID) bool
   122  
   123  	Port uint16
   124  
   125  	// LogFile is the log file name of the p2p node at runtime.
   126  	//
   127  	// The default value is empty so that the default log writer
   128  	// is the system standard output.
   129  	LogFile string
   130  
   131  	// LogVerbosity is the log verbosity of the p2p node at runtime.
   132  	//
   133  	// The default verbosity is INFO.
   134  	LogVerbosity log.Lvl
   135  }
   136  
   137  // nodeConfigJSON is used to encode and decode NodeConfig as JSON by encoding
   138  // all fields as strings
   139  type nodeConfigJSON struct {
   140  	ID              string   `json:"id"`
   141  	PrivateKey      string   `json:"private_key"`
   142  	Name            string   `json:"name"`
   143  	Lifecycles      []string `json:"lifecycles"`
   144  	Properties      []string `json:"properties"`
   145  	EnableMsgEvents bool     `json:"enable_msg_events"`
   146  	Port            uint16   `json:"port"`
   147  	LogFile         string   `json:"logfile"`
   148  	LogVerbosity    int      `json:"log_verbosity"`
   149  }
   150  
   151  // MarshalJSON implements the json.Marshaler interface by encoding the config
   152  // fields as strings
   153  func (n *NodeConfig) MarshalJSON() ([]byte, error) {
   154  	confJSON := nodeConfigJSON{
   155  		ID:              n.ID.String(),
   156  		Name:            n.Name,
   157  		Lifecycles:      n.Lifecycles,
   158  		Properties:      n.Properties,
   159  		Port:            n.Port,
   160  		EnableMsgEvents: n.EnableMsgEvents,
   161  		LogFile:         n.LogFile,
   162  		LogVerbosity:    int(n.LogVerbosity),
   163  	}
   164  	if n.PrivateKey != nil {
   165  		confJSON.PrivateKey = hex.EncodeToString(crypto.FromECDSA(n.PrivateKey))
   166  	}
   167  	return json.Marshal(confJSON)
   168  }
   169  
   170  // UnmarshalJSON implements the json.Unmarshaler interface by decoding the json
   171  // string values into the config fields
   172  func (n *NodeConfig) UnmarshalJSON(data []byte) error {
   173  	var confJSON nodeConfigJSON
   174  	if err := json.Unmarshal(data, &confJSON); err != nil {
   175  		return err
   176  	}
   177  
   178  	if confJSON.ID != "" {
   179  		if err := n.ID.UnmarshalText([]byte(confJSON.ID)); err != nil {
   180  			return err
   181  		}
   182  	}
   183  
   184  	if confJSON.PrivateKey != "" {
   185  		key, err := hex.DecodeString(confJSON.PrivateKey)
   186  		if err != nil {
   187  			return err
   188  		}
   189  		privKey, err := crypto.ToECDSA(key)
   190  		if err != nil {
   191  			return err
   192  		}
   193  		n.PrivateKey = privKey
   194  	}
   195  
   196  	n.Name = confJSON.Name
   197  	n.Lifecycles = confJSON.Lifecycles
   198  	n.Properties = confJSON.Properties
   199  	n.Port = confJSON.Port
   200  	n.EnableMsgEvents = confJSON.EnableMsgEvents
   201  	n.LogFile = confJSON.LogFile
   202  	n.LogVerbosity = log.Lvl(confJSON.LogVerbosity)
   203  
   204  	return nil
   205  }
   206  
   207  // Node returns the node descriptor represented by the config.
   208  func (n *NodeConfig) Node() *enode.Node {
   209  	return n.node
   210  }
   211  
   212  // RandomNodeConfig returns node configuration with a randomly generated ID and
   213  // PrivateKey
   214  func RandomNodeConfig() *NodeConfig {
   215  	prvkey, err := crypto.GenerateKey()
   216  	if err != nil {
   217  		panic("unable to generate key")
   218  	}
   219  
   220  	port, err := assignTCPPort()
   221  	if err != nil {
   222  		panic("unable to assign tcp port")
   223  	}
   224  
   225  	enodId := enode.PubkeyToIDV4(&prvkey.PublicKey)
   226  	return &NodeConfig{
   227  		PrivateKey:      prvkey,
   228  		ID:              enodId,
   229  		Name:            fmt.Sprintf("node_%s", enodId.String()),
   230  		Port:            port,
   231  		EnableMsgEvents: true,
   232  		LogVerbosity:    log.LvlInfo,
   233  	}
   234  }
   235  
   236  func assignTCPPort() (uint16, error) {
   237  	l, err := net.Listen("tcp", "127.0.0.1:0")
   238  	if err != nil {
   239  		return 0, err
   240  	}
   241  	l.Close()
   242  	_, port, err := net.SplitHostPort(l.Addr().String())
   243  	if err != nil {
   244  		return 0, err
   245  	}
   246  	p, err := strconv.ParseUint(port, 10, 16)
   247  	if err != nil {
   248  		return 0, err
   249  	}
   250  	return uint16(p), nil
   251  }
   252  
   253  // ServiceContext is a collection of options and methods which can be utilised
   254  // when starting services
   255  type ServiceContext struct {
   256  	RPCDialer
   257  
   258  	Config   *NodeConfig
   259  	Snapshot []byte
   260  }
   261  
   262  // RPCDialer is used when initialising services which need to connect to
   263  // other nodes in the network (for example a simulated Swarm node which needs
   264  // to connect to a Geth node to resolve ENS names)
   265  type RPCDialer interface {
   266  	DialRPC(id enode.ID) (*rpc.Client, error)
   267  }
   268  
   269  // LifecycleConstructor allows a Lifecycle to be constructed during node start-up.
   270  // While the service-specific package usually takes care of Lifecycle creation and registration,
   271  // for testing purposes, it is useful to be able to construct a Lifecycle on spot.
   272  type LifecycleConstructor func(ctx *ServiceContext, stack *node.Node) (node.Lifecycle, error)
   273  
   274  // LifecycleConstructors stores LifecycleConstructor functions to call during node start-up.
   275  type LifecycleConstructors map[string]LifecycleConstructor
   276  
   277  // lifecycleConstructorFuncs is a map of registered services which are used to boot devp2p
   278  // nodes
   279  var lifecycleConstructorFuncs = make(LifecycleConstructors)
   280  
   281  // RegisterLifecycles registers the given Services which can then be used to
   282  // start devp2p nodes using either the Exec or Docker adapters.
   283  //
   284  // It should be called in an init function so that it has the opportunity to
   285  // execute the services before main() is called.
   286  func RegisterLifecycles(lifecycles LifecycleConstructors) {
   287  	for name, f := range lifecycles {
   288  		if _, exists := lifecycleConstructorFuncs[name]; exists {
   289  			panic(fmt.Sprintf("node service already exists: %q", name))
   290  		}
   291  		lifecycleConstructorFuncs[name] = f
   292  	}
   293  
   294  	// now we have registered the services, run reexec.Init() which will
   295  	// potentially start one of the services if the current binary has
   296  	// been exec'd with argv[0] set to "p2p-node"
   297  	if reexec.Init() {
   298  		os.Exit(0)
   299  	}
   300  }
   301  
   302  // adds the host part to the configuration's ENR, signs it
   303  // creates and  the corresponding enode object to the configuration
   304  func (n *NodeConfig) initEnode(ip net.IP, tcpport int, udpport int) error {
   305  	enrIp := enr.IP(ip)
   306  	n.Record.Set(&enrIp)
   307  	enrTcpPort := enr.TCP(tcpport)
   308  	n.Record.Set(&enrTcpPort)
   309  	enrUdpPort := enr.UDP(udpport)
   310  	n.Record.Set(&enrUdpPort)
   311  
   312  	err := enode.SignV4(&n.Record, n.PrivateKey)
   313  	if err != nil {
   314  		return fmt.Errorf("unable to generate ENR: %v", err)
   315  	}
   316  	nod, err := enode.New(enode.V4ID{}, &n.Record)
   317  	if err != nil {
   318  		return fmt.Errorf("unable to create enode: %v", err)
   319  	}
   320  	log.Trace("simnode new", "record", n.Record)
   321  	n.node = nod
   322  	return nil
   323  }
   324  
   325  func (n *NodeConfig) initDummyEnode() error {
   326  	return n.initEnode(net.IPv4(127, 0, 0, 1), int(n.Port), 0)
   327  }