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 }