geth p2p.Server는 어떻게 실행될까?

최호준·2022년 10월 8일
0

먼저 어떻게 생성되는지 살펴보자

geth클라이언트는 cmd/geth/main.go에 진입점이 존재한다.

  • main.go의 geth가 app.Action으로 호출되면 geth 함수가 호출되고 이 함수는 프로그램이 종료될 때까지 block된다.
  • makeFUllNode에서 return된 stack은 *node.Node타입이다.
cmd/geth/main.go

// geth is the main entry point into the system if no special subcommand is ran.
// It creates a default node based on the command line arguments and runs it in
// blocking mode, waiting for it to be shut down.
func geth(ctx *cli.Context) error {
	// commandline argu를 처리한다.
	prepare(ctx)
	// makeFullNode는 geth configuration을 갖는 *node.Node와  ethapi.Backend를 return한다.
	stack, backend := makeFullNode(ctx)
	defer stack.Close()
	startNode(ctx, stack, backend, false)
    // 프로그램이 종료될 때까지 block 
	stack.Wait()
	return nil
}
  • makeFUllNode
// makeFullNode loads geth configuration and creates the Ethereum backend.
func makeFullNode(ctx *cli.Context) (*node.Node, ethapi.Backend) {
	stack, cfg := makeConfigNode(ctx)
    
	// ...
	
    return stack, backend
}
  • makeConfigNode
// makeConfigNode loads geth configuration and creates a blank node instance.
func makeConfigNode(ctx *cli.Context) (*node.Node, gethConfig) {
	// Load defaults.
	cfg := gethConfig{
		Eth:     ethconfig.Defaults,
		Node:    defaultNodeConfig(),
		Metrics: metrics.DefaultConfig,
	}

	// Load config file.
	// configFileFlag.Name이 존재하면 cfg로 load한다
	if file := ctx.String(configFileFlag.Name); file != "" {
		if err := loadConfig(file, &cfg); err != nil {
			utils.Fatalf("%v", err)
		}
	}

	// Apply flags. 
	utils.SetNodeConfig(ctx, &cfg.Node)
    // 여기서 cfg를 기반으로 Node를 생성한다.
	stack, err := node.New(&cfg.Node)
	// ...
	return stack, cfg
}
  • Node가 생성되는 코드는 다음과 같다.
  • Node를 생성하면서 Node의 필드인 p2p.Server도 같이 생성한다.

// New creates a new P2P node, ready for protocol registration.
func New(conf *Config) (*Node, error) {
	// conf의 복사본을 만들어서 현재 작업 디렉터리에 대한 변경 사항이 노드에 영향을 미치지 않도록한다.
	confCopy := *conf
	conf = &confCopy
	
	// 만약 flag에 --datadir을 설정했다면 이 부분이 실행된다.
	if conf.DataDir != "" {
		absdatadir, err := filepath.Abs(conf.DataDir)
		if err != nil {
			return nil, err
		}
		conf.DataDir = absdatadir
	}
    // ...
	node := &Node{
		config:        conf,
		inprocHandler: rpc.NewServer(),
		eventmux:      new(event.TypeMux),
		log:           conf.Logger,
		stop:          make(chan struct{}),
		// 바로 여기서 p2p.Server를 만들어준다.
		server:        &p2p.Server{Config: conf.P2P},
		databases:     make(map[*closeTrackingDB]struct{}),
	}
	// ... 
	// --datadir에 있는 keystore폴더를 읽는다 
	keyDir, isEphem, err := getKeyStoreDir(conf)
	if err != nil {
		return nil, err
	}
	node.keyDir = keyDir
	node.keyDirTemp = isEphem
    // ... 
	// Initialize the p2p server. This creates the node key and discovery databases.
    // node.config.NodeKey()는 datadir/geth/nodekey를 읽는다.
	// 만약 파일이 없다면  ecdsa.GenerateKey(S256(), rand.Reader)로 새로운 private key를 return한다.
	// nodekey는 *ecdsa.PrivateKey 타입이다.
	// 만약 load한  c.P2P.PrivateKey가 존재한다면 이 key를 사용한다.
	node.server.Config.PrivateKey = node.config.NodeKey()
	// ... 
    
	return node, nil
}

startNode- server 실행

  • 위의 geth함수에서 makeFUllNode호출의 결과로 Node를 받았다면 이제 Node를 실행할 차례이다.
func geth(ctx *cli.Context) error {
	// ...
	stack, backend := makeFullNode(ctx)
	defer stack.Close()
	// 여기서 실행한다.
	startNode(ctx, stack, backend, false)
	stack.Wait()
	return nil
}
  • startNode를 살펴보자
// startNode boots up the system node and all registered protocols, after which
// it unlocks any requested accounts, and starts the RPC/IPC interfaces and the
// miner.
func startNode(ctx *cli.Context, stack *node.Node, backend ethapi.Backend, isConsole bool) {
	debug.Memsize.Add("node", stack)
	// Start up the node itself
	// utils패키지의 StartNode를 호출 
	utils.StartNode(ctx, stack, isConsole)
    // ...
}
  • utils.StartNode는 다음과 같다.
func StartNode(ctx *cli.Context, stack *node.Node, isConsole bool) {
	if err := stack.Start(); err != nil {
		Fatalf("Error starting protocol stack: %v", err)
	}
    // ... 	
}
  • Start는 단 한 번만 호출되어야 한다.
  • Start에서는 드디어 p2p네트워킹을 시작한다.

// Start starts all registered lifecycles, RPC services and p2p networking.
// Node can only be started once.
func (n *Node) Start() error {
	n.startStopLock.Lock()
	defer n.startStopLock.Unlock()

	n.lock.Lock()
    // ... 
	n.state = runningState
	// open networking and RPC endpoints
	err := n.openEndpoints()
	// ... 
	return err
}
  • n.server는 p2p 패키지의 Server Struct이다.
    Server구조체가 어떻게 작동하는지는 여기서 (1,2) 다루었다.
// openEndpoints starts all network and RPC endpoints.
func (n *Node) openEndpoints() error {
	// start networking endpoints
	n.log.Info("Starting peer-to-peer node", "instance", n.server.Name)
	// n.server는 p2p 패키지의 Server struct이다. 
	// server는 handshake, pinpong, findnode등등 네트워크의 노드와 커뮤니테이션을 진행한다.
	if err := n.server.Start(); err != nil {
		return convertFileLockError(err)
	}
	// ...
	return err
}

0개의 댓글