github.com/yinchengtsinghua/golang-Eos-dpos-Ethereum@v0.0.0-20190121132951-92cc4225ed8e/p2p/simulations/adapters/exec.go (about)

     1  
     2  //此源码被清华学神尹成大魔王专业翻译分析并修改
     3  //尹成QQ77025077
     4  //尹成微信18510341407
     5  //尹成所在QQ群721929980
     6  //尹成邮箱 yinc13@mails.tsinghua.edu.cn
     7  //尹成毕业于清华大学,微软区块链领域全球最有价值专家
     8  //https://mvp.microsoft.com/zh-cn/PublicProfile/4033620
     9  //版权所有2017 Go Ethereum作者
    10  //此文件是Go以太坊库的一部分。
    11  //
    12  //Go-Ethereum库是免费软件:您可以重新分发它和/或修改
    13  //根据GNU发布的较低通用公共许可证的条款
    14  //自由软件基金会,或者许可证的第3版,或者
    15  //(由您选择)任何更高版本。
    16  //
    17  //Go以太坊图书馆的发行目的是希望它会有用,
    18  //但没有任何保证;甚至没有
    19  //适销性或特定用途的适用性。见
    20  //GNU较低的通用公共许可证,了解更多详细信息。
    21  //
    22  //你应该收到一份GNU较低级别的公共许可证副本
    23  //以及Go以太坊图书馆。如果没有,请参见<http://www.gnu.org/licenses/>。
    24  
    25  package adapters
    26  
    27  import (
    28  	"bufio"
    29  	"context"
    30  	"crypto/ecdsa"
    31  	"encoding/json"
    32  	"errors"
    33  	"fmt"
    34  	"io"
    35  	"net"
    36  	"os"
    37  	"os/exec"
    38  	"os/signal"
    39  	"path/filepath"
    40  	"strings"
    41  	"sync"
    42  	"syscall"
    43  	"time"
    44  
    45  	"github.com/docker/docker/pkg/reexec"
    46  	"github.com/ethereum/go-ethereum/log"
    47  	"github.com/ethereum/go-ethereum/node"
    48  	"github.com/ethereum/go-ethereum/p2p"
    49  	"github.com/ethereum/go-ethereum/p2p/discover"
    50  	"github.com/ethereum/go-ethereum/rpc"
    51  	"golang.org/x/net/websocket"
    52  )
    53  
    54  //Execadapter是一个节点适配器,通过执行
    55  //当前二进制文件作为子进程。
    56  //
    57  //使用init钩子以便子进程执行节点服务
    58  //(而不是main()函数通常执行的操作),请参见
    59  //有关详细信息,请参阅execp2pnode函数。
    60  type ExecAdapter struct {
    61  //basedir是每个目录下的数据目录
    62  //创建模拟节点。
    63  	BaseDir string
    64  
    65  	nodes map[discover.NodeID]*ExecNode
    66  }
    67  
    68  //newexecadapter返回一个execadapter,该execadapter将节点数据存储在
    69  //给定基目录的子目录
    70  func NewExecAdapter(baseDir string) *ExecAdapter {
    71  	return &ExecAdapter{
    72  		BaseDir: baseDir,
    73  		nodes:   make(map[discover.NodeID]*ExecNode),
    74  	}
    75  }
    76  
    77  //name返回用于日志记录的适配器的名称
    78  func (e *ExecAdapter) Name() string {
    79  	return "exec-adapter"
    80  }
    81  
    82  //newnode使用给定的配置返回新的execnode
    83  func (e *ExecAdapter) NewNode(config *NodeConfig) (Node, error) {
    84  	if len(config.Services) == 0 {
    85  		return nil, errors.New("node must have at least one service")
    86  	}
    87  	for _, service := range config.Services {
    88  		if _, exists := serviceFuncs[service]; !exists {
    89  			return nil, fmt.Errorf("unknown node service %q", service)
    90  		}
    91  	}
    92  
    93  //使用ID的前12个字符创建节点目录
    94  //因为unix套接字路径不能超过256个字符
    95  	dir := filepath.Join(e.BaseDir, config.ID.String()[:12])
    96  	if err := os.Mkdir(dir, 0755); err != nil {
    97  		return nil, fmt.Errorf("error creating node directory: %s", err)
    98  	}
    99  
   100  //生成配置
   101  	conf := &execNodeConfig{
   102  		Stack: node.DefaultConfig,
   103  		Node:  config,
   104  	}
   105  	conf.Stack.DataDir = filepath.Join(dir, "data")
   106  	conf.Stack.WSHost = "127.0.0.1"
   107  	conf.Stack.WSPort = 0
   108  	conf.Stack.WSOrigins = []string{"*"}
   109  	conf.Stack.WSExposeAll = true
   110  	conf.Stack.P2P.EnableMsgEvents = false
   111  	conf.Stack.P2P.NoDiscovery = true
   112  	conf.Stack.P2P.NAT = nil
   113  	conf.Stack.NoUSB = true
   114  
   115  //监听本地主机端口,当我们
   116  //初始化nodeconfig(通常是随机端口)
   117  	conf.Stack.P2P.ListenAddr = fmt.Sprintf(":%d", config.Port)
   118  
   119  	node := &ExecNode{
   120  		ID:      config.ID,
   121  		Dir:     dir,
   122  		Config:  conf,
   123  		adapter: e,
   124  	}
   125  	node.newCmd = node.execCommand
   126  	e.nodes[node.ID] = node
   127  	return node, nil
   128  }
   129  
   130  //exec node通过执行当前二进制文件和
   131  //运行配置的服务
   132  type ExecNode struct {
   133  	ID     discover.NodeID
   134  	Dir    string
   135  	Config *execNodeConfig
   136  	Cmd    *exec.Cmd
   137  	Info   *p2p.NodeInfo
   138  
   139  	adapter *ExecAdapter
   140  	client  *rpc.Client
   141  	wsAddr  string
   142  	newCmd  func() *exec.Cmd
   143  	key     *ecdsa.PrivateKey
   144  }
   145  
   146  //addr返回节点的enode url
   147  func (n *ExecNode) Addr() []byte {
   148  	if n.Info == nil {
   149  		return nil
   150  	}
   151  	return []byte(n.Info.Enode)
   152  }
   153  
   154  //客户端返回一个rpc.client,可用于与
   155  //基础服务(节点启动后设置)
   156  func (n *ExecNode) Client() (*rpc.Client, error) {
   157  	return n.client, nil
   158  }
   159  
   160  //start exec是将ID和服务作为命令行参数传递的节点
   161  //节点配置在节点配置环境中编码为json
   162  //变量
   163  func (n *ExecNode) Start(snapshots map[string][]byte) (err error) {
   164  	if n.Cmd != nil {
   165  		return errors.New("already started")
   166  	}
   167  	defer func() {
   168  		if err != nil {
   169  			log.Error("node failed to start", "err", err)
   170  			n.Stop()
   171  		}
   172  	}()
   173  
   174  //对包含快照的配置副本进行编码
   175  	confCopy := *n.Config
   176  	confCopy.Snapshots = snapshots
   177  	confCopy.PeerAddrs = make(map[string]string)
   178  	for id, node := range n.adapter.nodes {
   179  		confCopy.PeerAddrs[id.String()] = node.wsAddr
   180  	}
   181  	confData, err := json.Marshal(confCopy)
   182  	if err != nil {
   183  		return fmt.Errorf("error generating node config: %s", err)
   184  	}
   185  
   186  //为stderr使用管道,这样我们都可以将节点的stderr复制到
   187  //os.stderr并从日志中读取websocket地址
   188  	stderrR, stderrW := io.Pipe()
   189  	stderr := io.MultiWriter(os.Stderr, stderrW)
   190  
   191  //启动节点
   192  	cmd := n.newCmd()
   193  	cmd.Stdout = os.Stdout
   194  	cmd.Stderr = stderr
   195  	cmd.Env = append(os.Environ(), fmt.Sprintf("_P2P_NODE_CONFIG=%s", confData))
   196  	if err := cmd.Start(); err != nil {
   197  		return fmt.Errorf("error starting node: %s", err)
   198  	}
   199  	n.Cmd = cmd
   200  
   201  //从stderr日志中读取websocket地址
   202  	var wsAddr string
   203  	wsAddrC := make(chan string)
   204  	go func() {
   205  		s := bufio.NewScanner(stderrR)
   206  		for s.Scan() {
   207  			if strings.Contains(s.Text(), "WebSocket endpoint opened") {
   208  				wsAddrC <- wsAddrPattern.FindString(s.Text())
   209  			}
   210  		}
   211  	}()
   212  	select {
   213  	case wsAddr = <-wsAddrC:
   214  		if wsAddr == "" {
   215  			return errors.New("failed to read WebSocket address from stderr")
   216  		}
   217  	case <-time.After(10 * time.Second):
   218  		return errors.New("timed out waiting for WebSocket address on stderr")
   219  	}
   220  
   221  //创建RPC客户端并加载节点信息
   222  	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
   223  	defer cancel()
   224  	client, err := rpc.DialWebsocket(ctx, wsAddr, "")
   225  	if err != nil {
   226  		return fmt.Errorf("error dialing rpc websocket: %s", err)
   227  	}
   228  	var info p2p.NodeInfo
   229  	if err := client.CallContext(ctx, &info, "admin_nodeInfo"); err != nil {
   230  		return fmt.Errorf("error getting node info: %s", err)
   231  	}
   232  	n.client = client
   233  	n.wsAddr = wsAddr
   234  	n.Info = &info
   235  
   236  	return nil
   237  }
   238  
   239  //exec command返回一个命令,该命令通过exeng在本地运行节点
   240  //当前二进制文件,但将argv[0]设置为“p2p node”,以便子级
   241  //运行execp2pnode
   242  func (n *ExecNode) execCommand() *exec.Cmd {
   243  	return &exec.Cmd{
   244  		Path: reexec.Self(),
   245  		Args: []string{"p2p-node", strings.Join(n.Config.Node.Services, ","), n.ID.String()},
   246  	}
   247  }
   248  
   249  //stop首先发送sigterm,然后在节点
   250  //在5秒内没有停止
   251  func (n *ExecNode) Stop() error {
   252  	if n.Cmd == nil {
   253  		return nil
   254  	}
   255  	defer func() {
   256  		n.Cmd = nil
   257  	}()
   258  
   259  	if n.client != nil {
   260  		n.client.Close()
   261  		n.client = nil
   262  		n.wsAddr = ""
   263  		n.Info = nil
   264  	}
   265  
   266  	if err := n.Cmd.Process.Signal(syscall.SIGTERM); err != nil {
   267  		return n.Cmd.Process.Kill()
   268  	}
   269  	waitErr := make(chan error)
   270  	go func() {
   271  		waitErr <- n.Cmd.Wait()
   272  	}()
   273  	select {
   274  	case err := <-waitErr:
   275  		return err
   276  	case <-time.After(5 * time.Second):
   277  		return n.Cmd.Process.Kill()
   278  	}
   279  }
   280  
   281  //nodeinfo返回有关节点的信息
   282  func (n *ExecNode) NodeInfo() *p2p.NodeInfo {
   283  	info := &p2p.NodeInfo{
   284  		ID: n.ID.String(),
   285  	}
   286  	if n.client != nil {
   287  		n.client.Call(&info, "admin_nodeInfo")
   288  	}
   289  	return info
   290  }
   291  
   292  //serverpc通过拨
   293  //节点的WebSocket地址和连接两个连接
   294  func (n *ExecNode) ServeRPC(clientConn net.Conn) error {
   295  conn, err := websocket.Dial(n.wsAddr, "", "http://“本地主机”
   296  	if err != nil {
   297  		return err
   298  	}
   299  	var wg sync.WaitGroup
   300  	wg.Add(2)
   301  	join := func(src, dst net.Conn) {
   302  		defer wg.Done()
   303  		io.Copy(dst, src)
   304  //关闭目标连接的写入端
   305  		if cw, ok := dst.(interface {
   306  			CloseWrite() error
   307  		}); ok {
   308  			cw.CloseWrite()
   309  		} else {
   310  			dst.Close()
   311  		}
   312  	}
   313  	go join(conn, clientConn)
   314  	go join(clientConn, conn)
   315  	wg.Wait()
   316  	return nil
   317  }
   318  
   319  //快照通过调用
   320  //模拟快照RPC方法
   321  func (n *ExecNode) Snapshots() (map[string][]byte, error) {
   322  	if n.client == nil {
   323  		return nil, errors.New("RPC not started")
   324  	}
   325  	var snapshots map[string][]byte
   326  	return snapshots, n.client.Call(&snapshots, "simulation_snapshot")
   327  }
   328  
   329  func init() {
   330  //注册reexec函数以在当前
   331  //二进制作为“p2p节点”执行
   332  	reexec.Register("p2p-node", execP2PNode)
   333  }
   334  
   335  //ExecOnDeconfig用于序列化节点配置,以便
   336  //作为JSON编码的环境变量传递给子进程
   337  type execNodeConfig struct {
   338  	Stack     node.Config       `json:"stack"`
   339  	Node      *NodeConfig       `json:"node"`
   340  	Snapshots map[string][]byte `json:"snapshots,omitempty"`
   341  	PeerAddrs map[string]string `json:"peer_addrs,omitempty"`
   342  }
   343  
   344  //ExternalIP获取外部IP地址,以便enode url可用
   345  func ExternalIP() net.IP {
   346  	addrs, err := net.InterfaceAddrs()
   347  	if err != nil {
   348  		log.Crit("error getting IP address", "err", err)
   349  	}
   350  	for _, addr := range addrs {
   351  		if ip, ok := addr.(*net.IPNet); ok && !ip.IP.IsLoopback() && !ip.IP.IsLinkLocalUnicast() {
   352  			return ip.IP
   353  		}
   354  	}
   355  	log.Warn("unable to determine explicit IP address, falling back to loopback")
   356  	return net.IP{127, 0, 0, 1}
   357  }
   358  
   359  //执行当前二进制文件时,execp2pnode启动devp2p节点
   360  //argv[0]为“p2p节点”,从argv[1]/argv[2]读取服务/id
   361  //以及_p2p_node_config环境变量中的节点配置
   362  func execP2PNode() {
   363  	glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.LogfmtFormat()))
   364  	glogger.Verbosity(log.LvlInfo)
   365  	log.Root().SetHandler(glogger)
   366  
   367  //从argv读取服务
   368  	serviceNames := strings.Split(os.Args[1], ",")
   369  
   370  //解码配置
   371  	confEnv := os.Getenv("_P2P_NODE_CONFIG")
   372  	if confEnv == "" {
   373  		log.Crit("missing _P2P_NODE_CONFIG")
   374  	}
   375  	var conf execNodeConfig
   376  	if err := json.Unmarshal([]byte(confEnv), &conf); err != nil {
   377  		log.Crit("error decoding _P2P_NODE_CONFIG", "err", err)
   378  	}
   379  	conf.Stack.P2P.PrivateKey = conf.Node.PrivateKey
   380  	conf.Stack.Logger = log.New("node.id", conf.Node.ID.String())
   381  
   382  	if strings.HasPrefix(conf.Stack.P2P.ListenAddr, ":") {
   383  		conf.Stack.P2P.ListenAddr = ExternalIP().String() + conf.Stack.P2P.ListenAddr
   384  	}
   385  	if conf.Stack.WSHost == "0.0.0.0" {
   386  		conf.Stack.WSHost = ExternalIP().String()
   387  	}
   388  
   389  //初始化devp2p堆栈
   390  	stack, err := node.New(&conf.Stack)
   391  	if err != nil {
   392  		log.Crit("error creating node stack", "err", err)
   393  	}
   394  
   395  //注册服务,将它们收集到地图中,以便我们可以包装
   396  //它们在快照服务中
   397  	services := make(map[string]node.Service, len(serviceNames))
   398  	for _, name := range serviceNames {
   399  		serviceFunc, exists := serviceFuncs[name]
   400  		if !exists {
   401  			log.Crit("unknown node service", "name", name)
   402  		}
   403  		constructor := func(nodeCtx *node.ServiceContext) (node.Service, error) {
   404  			ctx := &ServiceContext{
   405  				RPCDialer:   &wsRPCDialer{addrs: conf.PeerAddrs},
   406  				NodeContext: nodeCtx,
   407  				Config:      conf.Node,
   408  			}
   409  			if conf.Snapshots != nil {
   410  				ctx.Snapshot = conf.Snapshots[name]
   411  			}
   412  			service, err := serviceFunc(ctx)
   413  			if err != nil {
   414  				return nil, err
   415  			}
   416  			services[name] = service
   417  			return service, nil
   418  		}
   419  		if err := stack.Register(constructor); err != nil {
   420  			log.Crit("error starting service", "name", name, "err", err)
   421  		}
   422  	}
   423  
   424  //注册快照服务
   425  	if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
   426  		return &snapshotService{services}, nil
   427  	}); err != nil {
   428  		log.Crit("error starting snapshot service", "err", err)
   429  	}
   430  
   431  //启动堆栈
   432  	if err := stack.Start(); err != nil {
   433  		log.Crit("error stating node stack", "err", err)
   434  	}
   435  
   436  //如果我们得到一个sigterm信号,就停止堆栈
   437  	go func() {
   438  		sigc := make(chan os.Signal, 1)
   439  		signal.Notify(sigc, syscall.SIGTERM)
   440  		defer signal.Stop(sigc)
   441  		<-sigc
   442  		log.Info("Received SIGTERM, shutting down...")
   443  		stack.Stop()
   444  	}()
   445  
   446  //等待堆栈退出
   447  	stack.Wait()
   448  }
   449  
   450  //SnapshotService是一个node.service,它包装了服务列表和
   451  //公开API以生成这些服务的快照
   452  type snapshotService struct {
   453  	services map[string]node.Service
   454  }
   455  
   456  func (s *snapshotService) APIs() []rpc.API {
   457  	return []rpc.API{{
   458  		Namespace: "simulation",
   459  		Version:   "1.0",
   460  		Service:   SnapshotAPI{s.services},
   461  	}}
   462  }
   463  
   464  func (s *snapshotService) Protocols() []p2p.Protocol {
   465  	return nil
   466  }
   467  
   468  func (s *snapshotService) Start(*p2p.Server) error {
   469  	return nil
   470  }
   471  
   472  func (s *snapshotService) Stop() error {
   473  	return nil
   474  }
   475  
   476  //Snapshotapi提供了一个RPC方法来创建服务的快照
   477  type SnapshotAPI struct {
   478  	services map[string]node.Service
   479  }
   480  
   481  func (api SnapshotAPI) Snapshot() (map[string][]byte, error) {
   482  	snapshots := make(map[string][]byte)
   483  	for name, service := range api.services {
   484  		if s, ok := service.(interface {
   485  			Snapshot() ([]byte, error)
   486  		}); ok {
   487  			snap, err := s.Snapshot()
   488  			if err != nil {
   489  				return nil, err
   490  			}
   491  			snapshots[name] = snap
   492  		}
   493  	}
   494  	return snapshots, nil
   495  }
   496  
   497  type wsRPCDialer struct {
   498  	addrs map[string]string
   499  }
   500  
   501  //DialRPC通过创建WebSocket RPC来实现RpcDialer接口
   502  //给定节点的客户端
   503  func (w *wsRPCDialer) DialRPC(id discover.NodeID) (*rpc.Client, error) {
   504  	addr, ok := w.addrs[id.String()]
   505  	if !ok {
   506  		return nil, fmt.Errorf("unknown node: %s", id)
   507  	}
   508  return rpc.DialWebsocket(context.Background(), addr, "http://“本地主机”
   509  }