[ethereum源码分析](4) ethereum运行开启console

前言

上一章我们介绍了  ethereum初始化指令 ,包括了系统是如何调用指令和指令的执行。在本章节我们将会介绍 geth --datadir dev/data/02 --networkid 666 console 指令。

指令分析

指令: geth --datadir dev/data/02 --networkid 666 console  

介绍:上面的指令主要的工作为:

  • 运行eth程序
  • 开启控制台

分析:

  •  --datadir :指定eth的数据存储地址为 dev/data/02 
  •  --networkid :指定eth的网络编号为 666 ,与其他eth网络区分,这个是建立公网的关键。

代码分析

接下来就让我们进入代码分析阶段,至于系统是如何执行命令的在前一章已经介绍过了,这里就不多在做介绍。

通过上一章的介绍我们知道,eth系统会执行以下的指令:

consoleCommand = cli.Command{
        Action:   utils.MigrateFlags( localConsole ),
        Name:     "console",
        Usage:    "Start an interactive JavaScript environment",
        Flags:    append(append(append(nodeFlags, rpcFlags...), consoleFlags...), whisperFlags...),
        Category: "CONSOLE COMMANDS",
        Description: `
The Geth console is an interactive shell for the JavaScript runtime environment
which exposes a node admin interface as well as the Ðapp JavaScript API.
See https://github.com/ethereum/go-ethereum/wiki/JavaScript-Console.`,
    }

上面就是我们 command 指令的数据结构,由上面的代码可知,最终代码会执行 localConsole 这个函数,那么下面就让我们来跟进这个函数:

// 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)
    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()
    console.Interactive()//进入命令行

    return nil
}

上面的代码首先执行了 makeFullNode(ctx) 。下面让我们来跟进代码:

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 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)
        }
        utils.RegisterShhService(stack, &cfg.Shh)
    }

    // Add the Ethereum Stats daemon if requested.
    if cfg.Ethstats.URL != "" {
        utils.RegisterEthStatsService(stack, cfg.Ethstats.URL)
    }
    return stack
}

 上面的代码首先构造了一个节点,那么他是如何去构造这个节点的呢,让我们来看一看 makeConfigNode(ctx) 这个函数:

func makeConfigNode(ctx *cli.Context) (*node.Node, gethConfig) {
    // Load defaults.
    cfg := gethConfig{
        Eth:       eth.DefaultConfig,//配置eth的一些基本信息,里面设置了NetWorkId(默认值为1,这也是eth的主网)的信息
        Shh:       whisper.DefaultConfig,//里面配置了两个参数:1.MaxMessageSize 2.MinimumAcceptedPOW(POW:Proof of Work,工作量证明),暂时不知道有什么用处?
        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)//这里设置了P2P,IPC,HTTP,WS,DataDir,KeyStoreDir的值
    stack, err := node.New(&cfg.Node)
    if err != nil {
        utils.Fatalf("Failed to create the protocol stack: %v", err)
    }
    utils.SetEthConfig(ctx, stack, &cfg.Eth)//这里设置NetWorkId的值
    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
}

当 makeFullNode(ctx)  执行完以后,接下来执行的就是 startNode(ctx, 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)//开启节点,主要做了开启P2P网络,开启UDP监听(监听30303端口),开启RLPx监听。

    // 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(&ethereum); 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))
        if err := ethereum.StartMining(true); err != nil {
            utils.Fatalf("Failed to start mining: %v", err)
        }
    }
}
原文地址:https://www.cnblogs.com/cafebabe-yun/p/9392341.html