geth클라이언트는 cmd/geth/main.go에 진입점이 존재한다.
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 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 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
}
// 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
}
func geth(ctx *cli.Context) error {
// ...
stack, backend := makeFullNode(ctx)
defer stack.Close()
// 여기서 실행한다.
startNode(ctx, stack, backend, false)
stack.Wait()
return nil
}
// 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)
// ...
}
func StartNode(ctx *cli.Context, stack *node.Node, isConsole bool) {
if err := stack.Start(); err != nil {
Fatalf("Error starting protocol stack: %v", err)
}
// ...
}
// 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
}
// 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
}