当我们要启动geth网络,我们需要运行命令: geth --datadir=~/gethData --networkid 1 console。这期间,操作分为2步:
1. 启动geth运行程序,初始化运行环境
2. 运行命令
一、初始化运行环境
//cmd/geth/main.go
func init() { // 初始化CLI(命令行界面),并启动geth app.Action = geth app.HideVersion = true // we have a command to print the version app.Copyright = "Copyright 2013-2018 The go-ethereum Authors" app.Commands = []cli.Command{ // See chaincmd.go: initCommand, importCommand, exportCommand, importPreimagesCommand, exportPreimagesCommand, copydbCommand, removedbCommand, dumpCommand, // See monitorcmd.go: monitorCommand, // See accountcmd.go: accountCommand, //账户命令处理hansh,例如创建账户 walletCommand, // See consolecmd.go: consoleCommand, //console命令处理对象,即启动一个geth网络时的命令处理类 attachCommand, javascriptCommand, // See misccmd.go: makecacheCommand, makedagCommand, versionCommand, bugCommand, licenseCommand, // See config.go dumpConfigCommand, } sort.Sort(cli.CommandsByName(app.Commands)) app.Flags = append(app.Flags, nodeFlags...) app.Flags = append(app.Flags, rpcFlags...) app.Flags = append(app.Flags, consoleFlags...) app.Flags = append(app.Flags, debug.Flags...) app.Flags = append(app.Flags, whisperFlags...)
//很重要,启动时需要优先执行的函数 app.Before = func(ctx *cli.Context) error { runtime.GOMAXPROCS(runtime.NumCPU()) if err := debug.Setup(ctx); err != nil { return err } // Cap the cache allowance and tune the garbage colelctor var mem gosigar.Mem if err := mem.Get(); err == nil { allowance := int(mem.Total / 1024 / 1024 / 3) if cache := ctx.GlobalInt(utils.CacheFlag.Name); cache > allowance { log.Warn("Sanitizing cache to Go's GC limits", "provided", cache, "updated", allowance) ctx.GlobalSet(utils.CacheFlag.Name, strconv.Itoa(allowance)) } } // Ensure Go's GC ignores the database cache for trigger percentage cache := ctx.GlobalInt(utils.CacheFlag.Name) gogc := math.Max(20, math.Min(100, 100/(float64(cache)/1024))) log.Debug("Sanitizing Go's GC trigger", "percent", int(gogc)) godebug.SetGCPercent(int(gogc)) // Start system runtime metrics collection go metrics.CollectProcessMetrics(3 * time.Second) utils.SetupNetwork(ctx) return nil } app.After = func(ctx *cli.Context) error { debug.Exit() console.Stdin.Close() // Resets terminal mode. return nil } } ---------------------------------------------------------------------
//程序入口main func main() { if err := app.Run(os.Args); err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } }
---------------------------------------------------------------------
// Run is the entry point to the cli app. Parses the arguments slice and routes // to the proper flag/args combination func (a *App) Run(arguments []string) (err error) { a.Setup() //仅做简单的配置,例如auth,email,command,输出流等 shellComplete, arguments := checkShellCompleteFlag(a, arguments) // parse flags set, err := flagSet(a.Name, a.Flags) if err != nil { return err } set.SetOutput(ioutil.Discard) err = set.Parse(arguments[1:]) nerr := normalizeFlags(a.Flags, set) context := NewContext(a, set, nil) if nerr != nil { fmt.Fprintln(a.Writer, nerr) ShowAppHelp(context) return nerr } context.shellComplete = shellComplete if checkCompletions(context) { return nil } if err != nil { if a.OnUsageError != nil { err := a.OnUsageError(context, err, false) HandleExitCoder(err) return err } fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error()) ShowAppHelp(context) return err } if !a.HideHelp && checkHelp(context) { ShowAppHelp(context) return nil } if !a.HideVersion && checkVersion(context) { ShowVersion(context) return nil } if a.After != nil { //退出时,执行init函数指定的after函数 defer func() { if afterErr := a.After(context); afterErr != nil { if err != nil { err = NewMultiError(err, afterErr) } else { err = afterErr } } }() } if a.Before != nil { //执行init函数指定的before函数 beforeErr := a.Before(context) if beforeErr != nil { ShowAppHelp(context) HandleExitCoder(beforeErr) err = beforeErr return err } } args := context.Args() if args.Present() { name := args.First() c := a.Command(name) if c != nil { return c.Run(context) //由于我们输入了console命令,所以在这里开始执行 } } if a.Action == nil { a.Action = helpCommand.Action //如果默认命令为空,那么默认执行help命令 } // Run default Action err = HandleAction(a.Action, context) //执行默认命令 HandleExitCoder(err) return err }
二、执行 geth --datadir=~/gethData --networkid 1 console
// Run invokes the command given the context, parses ctx.Args() to generate command-specific flags
func (c Command) Run(ctx *Context) (err error) { if len(c.Subcommands) > 0 { return c.startApp(ctx) } if !c.HideHelp && (HelpFlag != BoolFlag{}) { // append help to flags c.Flags = append( c.Flags, HelpFlag, ) } set, err := flagSet(c.Name, c.Flags) //设置各种flag if err != nil { return err } set.SetOutput(ioutil.Discard)
//解析命令行 if c.SkipFlagParsing { err = set.Parse(append([]string{"--"}, ctx.Args().Tail()...)) } else if !c.SkipArgReorder { firstFlagIndex := -1 terminatorIndex := -1 for index, arg := range ctx.Args() { if arg == "--" { terminatorIndex = index break } else if arg == "-" { // Do nothing. A dash alone is not really a flag. continue } else if strings.HasPrefix(arg, "-") && firstFlagIndex == -1 { firstFlagIndex = index } } if firstFlagIndex > -1 { args := ctx.Args() regularArgs := make([]string, len(args[1:firstFlagIndex])) copy(regularArgs, args[1:firstFlagIndex]) var flagArgs []string if terminatorIndex > -1 { flagArgs = args[firstFlagIndex:terminatorIndex] regularArgs = append(regularArgs, args[terminatorIndex:]...) } else { flagArgs = args[firstFlagIndex:] } err = set.Parse(append(flagArgs, regularArgs...)) } else { err = set.Parse(ctx.Args().Tail()) } } else { err = set.Parse(ctx.Args().Tail()) } nerr := normalizeFlags(c.Flags, set) if nerr != nil { fmt.Fprintln(ctx.App.Writer, nerr) fmt.Fprintln(ctx.App.Writer) ShowCommandHelp(ctx, c.Name) return nerr } //创建上下文 context := NewContext(ctx.App, set, ctx) context.Command = c if checkCommandCompletions(context, c.Name) { return nil } if err != nil { if c.OnUsageError != nil { err := c.OnUsageError(context, err, false) HandleExitCoder(err) return err } fmt.Fprintln(context.App.Writer, "Incorrect Usage:", err.Error()) fmt.Fprintln(context.App.Writer) ShowCommandHelp(context, c.Name) return err } if checkCommandHelp(context, c.Name) { return nil } if c.After != nil { defer func() { afterErr := c.After(context) if afterErr != nil { HandleExitCoder(err) if err != nil { err = NewMultiError(err, afterErr) } else { err = afterErr } } }() } if c.Before != nil { err = c.Before(context) if err != nil { ShowCommandHelp(context, c.Name) HandleExitCoder(err) return err } } if c.Action == nil { c.Action = helpSubcommand.Action } err = HandleAction(c.Action, context) //开始执行命令 if err != nil { HandleExitCoder(err) } return err }
------------------------------------------------------------------------
// localConsole starts a new geth node, attaching a JavaScript console to it at the // same time. func localConsole(ctx *cli.Context) error { // Create and start the node based on the CLI flags node := makeFullNode(ctx) //创建一个节点 startNode(ctx, node) //启动该节点 defer node.Stop() // Attach to the newly started node and start the JavaScript console client, err := node.Attach() if err != nil { utils.Fatalf("Failed to attach to the inproc geth: %v", err) } config := console.Config{ DataDir: utils.MakeDataDir(ctx), DocRoot: ctx.GlobalString(utils.JSpathFlag.Name), Client: client, Preload: utils.MakeConsolePreloads(ctx), } console, err := console.New(config) //创建一个console实例 if err != nil { utils.Fatalf("Failed to start the JavaScript console: %v", err) } defer console.Stop(false) // If only a short execution was requested, evaluate and return if script := ctx.GlobalString(utils.ExecFlag.Name); script != "" { console.Evaluate(script) return nil } // Otherwise print the welcome screen and enter interactive mode console.Welcome() //显示wellcome信息 console.Interactive() //与用户交互 return nil }
----------------------------------------------------------------------------
//Node is a container on which services can be registered(node是一个容器,服务可以注册到node上)
type Node struct { eventmux *event.TypeMux // Event multiplexer used between the services of a stack config *Config accman *accounts.Manager //账户管理器 ephemeralKeystore string // if non-empty, the key directory that will be removed by Stop instanceDirLock flock.Releaser // prevents concurrent use of instance directory serverConfig p2p.Config //p2p配置 server *p2p.Server // Currently running P2P networking layer serviceFuncs []ServiceConstructor // Service constructors (in dependency order) services map[reflect.Type]Service // Currently running services rpcAPIs []rpc.API // List of APIs currently provided by the node inprocHandler *rpc.Server // In-process RPC request handler to process the API requests ipcEndpoint string // IPC endpoint to listen at (empty = IPC disabled) ipcListener net.Listener // IPC RPC listener socket to serve API requests ipcHandler *rpc.Server // IPC RPC request handler to process the API requests httpEndpoint string // HTTP endpoint (interface + port) to listen at (empty = HTTP disabled) httpWhitelist []string // HTTP RPC modules to allow through this endpoint httpListener net.Listener // HTTP RPC listener socket to server API requests httpHandler *rpc.Server // HTTP RPC request handler to process the API requests wsEndpoint string // Websocket endpoint (interface + port) to listen at (empty = websocket disabled) wsListener net.Listener // Websocket RPC listener socket to server API requests wsHandler *rpc.Server // Websocket RPC request handler to process the API requests stop chan struct{} // Channel to wait for termination notifications lock sync.RWMutex log log.Logger }
-----------------------------------------------------------------
func makeFullNode(ctx *cli.Context) *node.Node { stack, cfg := makeConfigNode(ctx) //创建节点 utils.RegisterEthService(stack, &cfg.Eth) //注册以太坊服务 if ctx.GlobalBool(utils.DashboardEnabledFlag.Name) { utils.RegisterDashboardService(stack, &cfg.Dashboard, gitCommit) }
//Whisper(低语者)是以太坊的一个功能扩展。 //以太坊的智能合约smart contract实现了分布式逻辑, //以太坊的Swarm实现了分布式存储,以太坊的Whisper实现了分布式消息)。 //Whisper将实现智能合约间的消息互通,届时可以实现功能更加复杂的DApp。 // Whisper must be explicitly enabled by specifying at least 1 whisper flag or in dev mode shhEnabled := enableWhisper(ctx) shhAutoEnabled := !ctx.GlobalIsSet(utils.WhisperEnabledFlag.Name) && ctx.GlobalIsSet(utils.DeveloperFlag.Name) if shhEnabled || shhAutoEnabled { if ctx.GlobalIsSet(utils.WhisperMaxMessageSizeFlag.Name) { cfg.Shh.MaxMessageSize = uint32(ctx.Int(utils.WhisperMaxMessageSizeFlag.Name)) } if ctx.GlobalIsSet(utils.WhisperMinPOWFlag.Name) { cfg.Shh.MinimumAcceptedPOW = ctx.Float64(utils.WhisperMinPOWFlag.Name) }
//注册ssh服务 utils.RegisterShhService(stack, &cfg.Shh) } // Add the Ethereum Stats daemon if requested.
//以太坊状态(Eth Stats)是一个用于跟踪以太坊网络状态的可视化平台, //他使用WebSockets技术从正在运行中的节点中接受统计信息并通过不同的规则来输出数据, //用户可以通过该网站直观的了解以太坊节点的运行情况。 if cfg.Ethstats.URL != "" { utils.RegisterEthStatsService(stack, cfg.Ethstats.URL) } return stack }
------------------------------------------------------------------------------------
// RegisterEthService adds an Ethereum client to the stack. (注册以太坊服务) func RegisterEthService(stack *node.Node, cfg *eth.Config) { var err error
//如果同步模式是轻量级的同步模式。 那么启动轻量级的客户端。 if cfg.SyncMode == downloader.LightSync { err = stack.Register(func(ctx *node.ServiceContext) (node.Service, error) { return les.New(ctx, cfg) }) } else { // 否则会启动全节点 err = stack.Register(func(ctx *node.ServiceContext) (node.Service, error) { fullNode, err := eth.New(ctx, cfg) if fullNode != nil && cfg.LightServ > 0 { ls, _ := les.NewLesServer(fullNode, cfg) fullNode.AddLesServer(ls) } return fullNode, err }) } if err != nil { Fatalf("Failed to register the Ethereum service: %v", err) } }
以太坊有3中同步模式:
–fast Enable fast syncing through state downloads
–light Enable light client mode
–syncmode full
fast
模式,获取区块的header,获取区块的body,在同步到当前块之前不处理任何事务。下载的数据大小约为50GB(截止2018-02-04)。然后获得一个快照,此后,像full节点一样进行后面的同步操作。这种方法用得最多,目的在不要在意历史数据,将历史数据按照快照的方式,不逐一验证,沿着区块下载最近数据库中的交易,有可能丢失历史数据。此方法可能会对历史数据有部分丢失,但是不影响今后的使用。
//使用此模式时注意需要设置–cache,默认16M,建议设置为1G(1024)到2G(2048)
geth –fast –cache 512
light
模式,仅获取当前状态。验证元素需要向full节点发起相应的请求。
geth –light
- full 模式,从开始到结束,获取区块的header,获取区块的body,从创始块开始校验每一个元素,
需要下载所有区块数据信息。速度最慢,但是能获取到所有的历史数据。
geth –syncmode full
----------------------------------------------------------------------------
func defaultNodeConfig() node.Config { cfg := node.DefaultConfig cfg.Name = clientIdentifier cfg.Version = params.VersionWithCommit(gitCommit) cfg.HTTPModules = append(cfg.HTTPModules, "eth", "shh") cfg.WSModules = append(cfg.WSModules, "eth", "shh") cfg.IPCPath = "geth.ipc" return cfg }
//这个函数主要是通过配置文件和flag来生成整个系统的运行配置 func makeConfigNode(ctx *cli.Context) (*node.Node, gethConfig) { // Load defaults. cfg := gethConfig{ Eth: eth.DefaultConfig, Shh: whisper.DefaultConfig, Node: defaultNodeConfig(), Dashboard: dashboard.DefaultConfig, } // Load config file. if file := ctx.GlobalString(configFileFlag.Name); file != "" { if err := loadConfig(file, &cfg); err != nil { utils.Fatalf("%v", err) } } // Apply flags. utils.SetNodeConfig(ctx, &cfg.Node) stack, err := node.New(&cfg.Node) //创建一个node if err != nil { utils.Fatalf("Failed to create the protocol stack: %v", err) } utils.SetEthConfig(ctx, stack, &cfg.Eth) if ctx.GlobalIsSet(utils.EthStatsURLFlag.Name) { cfg.Ethstats.URL = ctx.GlobalString(utils.EthStatsURLFlag.Name) } utils.SetShhConfig(ctx, stack, &cfg.Shh) utils.SetDashboardConfig(ctx, &cfg.Dashboard) return stack, cfg }
---------------------------------------------------------
// New creates a new P2P node, ready for protocol registration. func New(conf *Config) (*Node, error) { // Copy config and resolve the datadir so future changes to the current // working directory don't affect the node. confCopy := *conf conf = &confCopy if conf.DataDir != "" { absdatadir, err := filepath.Abs(conf.DataDir) if err != nil { return nil, err } conf.DataDir = absdatadir } // Ensure that the instance name doesn't cause weird conflicts with // other files in the data directory. if strings.ContainsAny(conf.Name, `/\`) { return nil, errors.New(`Config.Name must not contain '/' or '\'`) } if conf.Name == datadirDefaultKeyStore { return nil, errors.New(`Config.Name cannot be "` + datadirDefaultKeyStore + `"`) } if strings.HasSuffix(conf.Name, ".ipc") { return nil, errors.New(`Config.Name cannot end in ".ipc"`) } // Ensure that the AccountManager method works before the node has started. // We rely on this in cmd/geth. am, ephemeralKeystore, err := makeAccountManager(conf) //AccountManager负责Account的相关管理操作 if err != nil { return nil, err } if conf.Logger == nil { conf.Logger = log.New() } // Note: any interaction with Config that would create/touch files // in the data directory or instance directory is delayed until Start. return &Node{ //组装node并返回 accman: am, ephemeralKeystore: ephemeralKeystore, config: conf, serviceFuncs: []ServiceConstructor{}, ipcEndpoint: conf.IPCEndpoint(), httpEndpoint: conf.HTTPEndpoint(), wsEndpoint: conf.WSEndpoint(), eventmux: new(event.TypeMux), log: conf.Logger, }, nil }
--------------------------------------------------------------
func makeAccountManager(conf *Config) (*accounts.Manager, string, error) { scryptN, scryptP, keydir, err := conf.AccountConfig() var ephemeral string
--------------------------------------------------------
// NewKeyStore creates a keystore for the given directory. func NewKeyStore(keydir string, scryptN, scryptP int) *KeyStore { keydir, _ = filepath.Abs(keydir) ks := &KeyStore{storage: &keyStorePassphrase{keydir, scryptN, scryptP}} ks.init(keydir) return ks }
------------------------------------------------------
func (ks *KeyStore) init(keydir string) { // Lock the mutex since the account cache might call back with events ks.mu.Lock() defer ks.mu.Unlock() // Initialize the set of unlocked keys and the account cache ks.unlocked = make(map[common.Address]*unlocked) ks.cache, ks.changes = newAccountCache(keydir) //从缓冲区中获取account,如没有则从datadir中获取 // TODO: In order for this finalizer to work, there must be no references // to ks. addressCache doesn't keep a reference but unlocked keys do, // so the finalizer will not trigger until all timed unlocks have expired. runtime.SetFinalizer(ks, func(m *KeyStore) { m.cache.close() }) // Create the initial list of wallets from the cache accs := ks.cache.accounts() //读取账户列表 ks.wallets = make([]accounts.Wallet, len(accs)) for i := 0; i < len(accs); i++ { ks.wallets[i] = &keystoreWallet{account: accs[i], keystore: ks} } }
----------------------------------------------
func (ac *accountCache) accounts() []accounts.Account { ac.maybeReload() //如果账户数据未被加载到内存中,则首先加载进来 ac.mu.Lock() defer ac.mu.Unlock() cpy := make([]accounts.Account, len(ac.all)) copy(cpy, ac.all) //然后copy一份,防止外面对缓存的账户做修改 return cpy }
-------------------------------------------------
func (ac *accountCache) maybeReload() { ac.mu.Lock() if ac.watcher.running { ac.mu.Unlock() return // A watcher is running and will keep the cache up-to-date. } if ac.throttle == nil { ac.throttle = time.NewTimer(0) } else { select { case <-ac.throttle.C: default: ac.mu.Unlock() return // The cache was reloaded recently. } } // No watcher running, start it. 使用goroutine启动一个watcher来监测keystore目录,防止其变化 ac.watcher.start() ac.throttle.Reset(minReloadInterval) ac.mu.Unlock() ac.scanAccounts() //从datadir/keystore中读取账户数据,例如address }
-----------------------------------------------------------
创建完node,接下来看一看start node
// 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) { debug.Memsize.Add("node", stack) // Start up the node itself utils.StartNode(stack) //启动节点 // Unlock any account specifically requested ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore) passwords := utils.MakePasswordList(ctx) unlocks := strings.Split(ctx.GlobalString(utils.UnlockedAccountFlag.Name), ",") for i, account := range unlocks { if trimmed := strings.TrimSpace(account); trimmed != "" { unlockAccount(ctx, ks, trimmed, i, passwords) } } // Register wallet event handlers to open and auto-derive wallets events := make(chan accounts.WalletEvent, 16) stack.AccountManager().Subscribe(events) go func() { // Create a chain state reader for self-derivation rpcClient, err := stack.Attach() if err != nil { utils.Fatalf("Failed to attach to self: %v", err) } stateReader := ethclient.NewClient(rpcClient) // Open any wallets already attached for _, wallet := range stack.AccountManager().Wallets() { if err := wallet.Open(""); err != nil { log.Warn("Failed to open wallet", "url", wallet.URL(), "err", err) } } // Listen for wallet event till termination for event := range events { switch event.Kind { case accounts.WalletArrived: if err := event.Wallet.Open(""); err != nil { log.Warn("New wallet appeared, failed to open", "url", event.Wallet.URL(), "err", err) } case accounts.WalletOpened: status, _ := event.Wallet.Status() log.Info("New wallet appeared", "url", event.Wallet.URL(), "status", status) if event.Wallet.URL().Scheme == "ledger" { event.Wallet.SelfDerive(accounts.DefaultLedgerBaseDerivationPath, stateReader) } else { event.Wallet.SelfDerive(accounts.DefaultBaseDerivationPath, stateReader) } case accounts.WalletDropped: log.Info("Old wallet dropped", "url", event.Wallet.URL()) event.Wallet.Close() } } }() // Start auxiliary services if enabled if ctx.GlobalBool(utils.MiningEnabledFlag.Name) || ctx.GlobalBool(utils.DeveloperFlag.Name) { // Mining only makes sense if a full Ethereum node is running if ctx.GlobalBool(utils.LightModeFlag.Name) || ctx.GlobalString(utils.SyncModeFlag.Name) == "light" { utils.Fatalf("Light clients do not support mining") } var ethereum *eth.Ethereum if err := stack.Service(ðereum); err != nil { utils.Fatalf("Ethereum service not running: %v", err) } // Use a reduced number of threads if requested if threads := ctx.GlobalInt(utils.MinerThreadsFlag.Name); threads > 0 { type threaded interface { SetThreads(threads int) } if th, ok := ethereum.Engine().(threaded); ok { th.SetThreads(threads) } } // Set the gas price to the limits from the CLI and start mining ethereum.TxPool().SetGasPrice(utils.GlobalBig(ctx, utils.GasPriceFlag.Name)) //给txPool设置gas price if err := ethereum.StartMining(true); err != nil { //启动挖矿 utils.Fatalf("Failed to start mining: %v", err) } } }
--------------------------
func StartNode(stack *node.Node) { if err := stack.Start(); err != nil { Fatalf("Error starting protocol stack: %v", err) } go func() { sigc := make(chan os.Signal, 1) signal.Notify(sigc, syscall.SIGINT, syscall.SIGTERM) defer signal.Stop(sigc) <-sigc log.Info("Got interrupt, shutting down...") go stack.Stop() for i := 10; i > 0; i-- { <-sigc if i > 1 { log.Warn("Already shutting down, interrupt more to panic.", "times", i-1) } } debug.Exit() // ensure trace and CPU profile data is flushed. debug.LoudPanic("boom") }() }
----------------------------------------
// 创建一个有生命的p2p节点,并启动它 func (n *Node) Start() error { n.lock.Lock() defer n.lock.Unlock() // Short circuit if the node's already running(如果节点已经运行,短路它) if n.server != nil { return ErrNodeRunning }
//datadir是p2p程序用于保存数据的文件目录 if err := n.openDataDir(); err != nil { return err } // Initialize the p2p server. This creates the node key and—— // discovery databases. n.serverConfig = n.config.P2P //p2p网络配置
//NodeKey检索当前已配置的节点的私钥,首先检查任何手动设置key, //返回一个在配置数据文件夹datadir/node中找到的key。 //如果没有找到关键字,就会生成一个新的。 //p2p/config.go里面为node生成一个key n.serverConfig.PrivateKey = n.config.NodeKey() n.serverConfig.Name = n.config.NodeName() //生成开发者模式p2p node的唯一标识 n.serverConfig.Logger = n.log //Logger是一个用于p2p.Server的自定义日志记录器。
//静态节点返回配置为静态节点的节点enode url列表。其中node URLs 从data.dir的文件夹中的json文件加载过来. if n.serverConfig.StaticNodes == nil { n.serverConfig.StaticNodes = n.config.StaticNodes() } if n.serverConfig.TrustedNodes == nil { n.serverConfig.TrustedNodes = n.config.TrustedNodes() } if n.serverConfig.NodeDatabase == "" { n.serverConfig.NodeDatabase = n.config.NodeDB() }
//启动p2p server并把节点的配置实例化server的配置 running := &p2p.Server{Config: n.serverConfig} n.log.Info("Starting peer-to-peer node", "instance", n.serverConfig.Name) // Otherwise copy and specialize the P2P configuration services := make(map[reflect.Type]Service) for _, constructor := range n.serviceFuncs { // Create a new context for the particular service ctx := &ServiceContext{ config: n.config, services: make(map[reflect.Type]Service), EventMux: n.eventmux, AccountManager: n.accman, } for kind, s := range services { // copy needed for threaded access ctx.services[kind] = s } // Construct and save the service service, err := constructor(ctx) if err != nil { return err } kind := reflect.TypeOf(service) if _, exists := services[kind]; exists { return &DuplicateServiceError{Kind: kind} } services[kind] = service } // Gather the protocols and start the freshly assembled P2P server for _, service := range services { running.Protocols = append(running.Protocols, service.Protocols()...) } if err := running.Start(); err != nil { //正式启动运行 return convertFileLockError(err) } // Start each of the services started := []reflect.Type{} for kind, service := range services { // Start the next service, stopping all previous upon failure if err := service.Start(running); err != nil { for _, kind := range started { services[kind].Stop() } running.Stop() return err } // Mark the service started for potential cleanup started = append(started, kind) } // Lastly start the configured RPC interfaces if err := n.startRPC(services); err != nil { //启动rpc for _, service := range services { service.Stop() } running.Stop() return err } // Finish initializing the startup n.services = services n.server = running n.stop = make(chan struct{}) return nil }
--------------------------------------
// Start starts running the server. // Servers can not be re-used after stopping. func (srv *Server) Start() (err error) { srv.lock.Lock() defer srv.lock.Unlock() if srv.running { return errors.New("server already running") } srv.running = true srv.log = srv.Config.Logger if srv.log == nil { srv.log = log.New() } srv.log.Info("Starting P2P networking") // static fields if srv.PrivateKey == nil { return fmt.Errorf("Server.PrivateKey must be set to a non-nil key") } if srv.newTransport == nil { srv.newTransport = newRLPX } if srv.Dialer == nil { srv.Dialer = TCPDialer{&net.Dialer{Timeout: defaultDialTimeout}} } srv.quit = make(chan struct{}) srv.addpeer = make(chan *conn) srv.delpeer = make(chan peerDrop) srv.posthandshake = make(chan *conn) srv.addstatic = make(chan *discover.Node) srv.removestatic = make(chan *discover.Node) srv.peerOp = make(chan peerOpFunc) srv.peerOpDone = make(chan struct{}) var ( conn *net.UDPConn sconn *sharedUDPConn realaddr *net.UDPAddr unhandled chan discover.ReadPacket ) if !srv.NoDiscovery || srv.DiscoveryV5 { addr, err := net.ResolveUDPAddr("udp", srv.ListenAddr) if err != nil { return err } conn, err = net.ListenUDP("udp", addr) if err != nil { return err } realaddr = conn.LocalAddr().(*net.UDPAddr) if srv.NAT != nil { if !realaddr.IP.IsLoopback() { go nat.Map(srv.NAT, srv.quit, "udp", realaddr.Port, realaddr.Port, "ethereum discovery") } // TODO: react to external IP changes over time. if ext, err := srv.NAT.ExternalIP(); err == nil { realaddr = &net.UDPAddr{IP: ext, Port: realaddr.Port} } } } if !srv.NoDiscovery && srv.DiscoveryV5 { unhandled = make(chan discover.ReadPacket, 100) sconn = &sharedUDPConn{conn, unhandled} } // node table if !srv.NoDiscovery { cfg := discover.Config{ PrivateKey: srv.PrivateKey, AnnounceAddr: realaddr, NodeDBPath: srv.NodeDatabase, NetRestrict: srv.NetRestrict, Bootnodes: srv.BootstrapNodes, Unhandled: unhandled, } ntab, err := discover.ListenUDP(conn, cfg) if err != nil { return err } srv.ntab = ntab }
//启动发现节点的监听任务 if srv.DiscoveryV5 { var ( ntab *discv5.Network err error ) if sconn != nil { ntab, err = discv5.ListenUDP(srv.PrivateKey, sconn, realaddr, "", srv.NetRestrict) //srv.NodeDatabase) } else { ntab, err = discv5.ListenUDP(srv.PrivateKey, conn, realaddr, "", srv.NetRestrict) //srv.NodeDatabase) } if err != nil { return err } if err := ntab.SetFallbackNodes(srv.BootstrapNodesV5); err != nil { return err } srv.DiscV5 = ntab } dynPeers := srv.maxDialedConns() dialer := newDialState(srv.StaticNodes, srv.BootstrapNodes, srv.ntab, dynPeers, srv.NetRestrict) // handshake,握手协议 srv.ourHandshake = &protoHandshake{Version: baseProtocolVersion, Name: srv.Name, ID: discover.PubkeyID(&srv.PrivateKey.PublicKey)} for _, p := range srv.Protocols { srv.ourHandshake.Caps = append(srv.ourHandshake.Caps, p.cap()) } // listen/dial if srv.ListenAddr != "" { if err := srv.startListening(); err != nil { return err } } if srv.NoDial && srv.ListenAddr == "" { srv.log.Warn("P2P server will be useless, neither dialing nor listening") } srv.loopWG.Add(1) go srv.run(dialer) srv.running = true return nil }
geth的启动流程十分复杂,代码量很多,因为有各种各样的数据结构,各种各样的flag,还有各种各样的服务,甚至牵扯到以太网整个架构,因此看一遍代码只能囫囵吞枣,能理解多少是多少吧。目前能够看懂的流程是:
1.加载配置文件,初始化各种config
2. 初始化CLI
3. 加载本地帐户列表
4. 创建一个geth节点
5. 启动geth节点的p2p服务