以太坊源码编译需要gov1.7以上,及C编译器,执行make geth 即可编译项目,编译后可执行的geth文件。

Makefile文件:

geth:build/env.sh go run build/ci.go install ./cmd/geth@echo "Done building."@echo "Run \"$(GOBIN)/geth\" to launch geth."

设置环境变量,执行build/cli.go文件,此文件会执行go install进行编译。

编译完成生成geth文件,直接执行即采用默认配置连接main网络,开始同步区块。

geth入口文件src/github.com/ethereum/go-ethereum/cmd/geth/main.go

func main() {if err := app.Run(os.Args); err != nil {fmt.Fprintln(os.Stderr, err)os.Exit(1)}
}

这里使用了一个第三方库,运行run会自动调用之前注册的before action after方法,地址:https://github.com/urfave/cli/tree/v2,很好用的一个库。


func init() {// Initialize the CLI app and start Gethapp.Action = gethapp.HideVersion = true // we have a command to print the versionapp.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,walletCommand,// See consolecmd.go:consoleCommand,attachCommand,javascriptCommand,// See misccmd.go:makecacheCommand,makedagCommand,versionCommand,bugCommand,licenseCommand,// See config.godumpConfigCommand,}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.Flags = append(app.Flags, metricsFlags...)app.Before = func(ctx *cli.Context) error {logdir := ""if ctx.GlobalBool(utils.DashboardEnabledFlag.Name) {logdir = (&node.Config{DataDir: utils.MakeDataDir(ctx)}).ResolvePath("logs")}if err := debug.Setup(ctx, logdir); err != nil {return err}// Cap the cache allowance and tune the garbage collectorvar mem gosigar.Memif 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 percentagecache := 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 metrics export if enabledutils.SetupMetrics(ctx)// Start system runtime metrics collectiongo metrics.CollectProcessMetrics(3 * time.Second)return nil}app.After = func(ctx *cli.Context) error {debug.Exit()console.Stdin.Close() // Resets terminal mode.return nil}
}

以上代码即提前注册了action方法,可以执行的command,命令接受的参数,及before方法,after方法。

直接执行geth,就相当于直接调用如下方法:

func geth(ctx *cli.Context) error {if args := ctx.Args(); len(args) > 0 {return fmt.Errorf("invalid command: %q", args[0])}node := makeFullNode(ctx)startNode(ctx, node)node.Wait()return nil
}

makeFullNode使用默认配置创建一个全节点的node,然后startNode启动node,最后等待结束指令。

下面先深入分析makeFullNode,主要是从配置文件或者命令行参数初始一个Node对象,看代码注释:


func makeFullNode(ctx *cli.Context) *node.Node {stack, cfg := makeConfigNode(ctx) //返回初始配置及node实例utils.RegisterEthService(stack, &cfg.Eth) //注册eth服务,但还未启动ethereum协议处理服务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 modeshhEnabled := 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)}if ctx.GlobalIsSet(utils.WhisperRestrictConnectionBetweenLightClientsFlag.Name) {cfg.Shh.RestrictConnectionBetweenLightClients = true}utils.RegisterShhService(stack, &cfg.Shh) //注册whisper服务}// Add the Ethereum Stats daemon if requested.if cfg.Ethstats.URL != "" {utils.RegisterEthStatsService(stack, cfg.Ethstats.URL) //注册统计监控服务}return stack
}

makeConfigNode代码如下


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)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
}
eth.DefaultConfig是geth的默认配置,代码:

// DefaultConfig contains default settings for use on the Ethereum main net.
var DefaultConfig = Config{SyncMode: downloader.FastSync,Ethash: ethash.Config{CacheDir:       "ethash",CachesInMem:    2,CachesOnDisk:   3,DatasetsInMem:  1,DatasetsOnDisk: 2,},NetworkId:     1,LightPeers:    100,DatabaseCache: 768,TrieCache:     256,TrieTimeout:   60 * time.Minute,MinerGasFloor: 8000000,MinerGasCeil:  8000000,MinerGasPrice: big.NewInt(params.GWei),MinerRecommit: 3 * time.Second,TxPool: core.DefaultTxPoolConfig,GPO: gasprice.Config{Blocks:     20,Percentile: 60,},
}

主要设置同步模式为fast模式,网络id为1,缓存的值,gas的值,及交易池的默认配置

makeConfigNode方法中还有一个从配置文件加载配置的逻辑

// Load config file.if file := ctx.GlobalString(configFileFlag.Name); file != "" {if err := loadConfig(file, &cfg); err != nil {utils.Fatalf("%v", err)}}

这个配置文件是一个toml的格式的文件,使用了第三方库github.com/naoina/toml,解析toml的库有很多,除了这个,还有另外一个库也挺好用,推荐给大家https://github.com/BurntSushi/toml

配置加载后,就调用stack, err := node.New(&cfg.Node) 初始一个node实例,即进入了node目录下的node.go文件,把New方法放这种规范的文件里,也是不错的规范。

返回node实例即配置信息后,即执行utils.RegisterEthService(stack, &cfg.Eth) 注册ethereum服务,注册逻辑就是将下面的方法注册到serviceFuns数组里

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
}

这个方法有个很重要的一行代码fullNode, err := eth.New(ctx, cfg),即创建一个ethereum实例,下面主要代码分析。

func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {// ...对一些配置进行验证,代码略// Assemble the Ethereum objectchainDb, err := CreateDB(ctx, config, "chaindata") //获取chaindata目录关联的db对象if err != nil {return nil, err}chainConfig, genesisHash, genesisErr := core.SetupGenesisBlock(chainDb, config.Genesis) //设置genesis块,加载默认genesis及写到goleveldb数据库,下面有代码详细分析if _, ok := genesisErr.(*params.ConfigCompatError); genesisErr != nil && !ok {return nil, genesisErr}log.Info("Initialised chain configuration", "config", chainConfig)eth := &Ethereum{config:         config,chainDb:        chainDb,chainConfig:    chainConfig,eventMux:       ctx.EventMux,accountManager: ctx.AccountManager,engine:         CreateConsensusEngine(ctx, chainConfig, &config.Ethash, config.MinerNotify, config.MinerNoverify, chainDb),shutdownChan:   make(chan bool),networkID:      config.NetworkId,gasPrice:       config.MinerGasPrice,etherbase:      config.Etherbase,bloomRequests:  make(chan chan *bloombits.Retrieval),bloomIndexer:   NewBloomIndexer(chainDb, params.BloomBitsBlocks, params.BloomConfirms),} //实例化ethereumlog.Info("Initialising Ethereum protocol", "versions", ProtocolVersions, "network", config.NetworkId)if !config.SkipBcVersionCheck {bcVersion := rawdb.ReadDatabaseVersion(chainDb)if bcVersion != core.BlockChainVersion && bcVersion != 0 { //对链的版本检查,当前是3return nil, fmt.Errorf("Blockchain DB version mismatch (%d / %d).\n", bcVersion, core.BlockChainVersion)}rawdb.WriteDatabaseVersion(chainDb, core.BlockChainVersion)}//...初始vm及cach的配置eth.blockchain, err = core.NewBlockChain(chainDb, cacheConfig, eth.chainConfig, eth.engine, vmConfig, eth.shouldPreserve)//创建一个blockchain实例if err != nil {return nil, err}// Rewind the chain in case of an incompatible config upgrade.if compat, ok := genesisErr.(*params.ConfigCompatError); ok {log.Warn("Rewinding chain to upgrade configuration", "err", compat)eth.blockchain.SetHead(compat.RewindTo)rawdb.WriteChainConfig(chainDb, genesisHash, chainConfig)}eth.bloomIndexer.Start(eth.blockchain) //启动bloom索引,一个多次hash按位比较的索引过滤器if config.TxPool.Journal != "" {config.TxPool.Journal = ctx.ResolvePath(config.TxPool.Journal)}eth.txPool = core.NewTxPool(config.TxPool, eth.chainConfig, eth.blockchain)// 创建交易池if eth.protocolManager, err = NewProtocolManager(eth.chainConfig, config.SyncMode, config.NetworkId, eth.eventMux, eth.txPool, eth.engine, eth.blockchain, chainDb); err != nil {return nil, err}eth.miner = miner.New(eth, eth.chainConfig, eth.EventMux(), eth.engine, config.MinerRecommit, config.MinerGasFloor, config.MinerGasCeil, eth.isLocalBlock) //创建矿工实例eth.miner.SetExtra(makeExtraData(config.MinerExtraData))eth.APIBackend = &EthAPIBackend{eth, nil}gpoParams := config.GPOif gpoParams.Default == nil {gpoParams.Default = config.MinerGasPrice}eth.APIBackend.gpo = gasprice.NewOracle(eth.APIBackend, gpoParams) //创建一个gasprice的语言机,提供推荐的gaspricereturn eth, nil
}

上面的ethereum服务会在node.start()时同时执行ethereum.start方法,代码如下:


// Start implements node.Service, starting all internal goroutines needed by the
// Ethereum protocol implementation.
func (s *Ethereum) Start(srvr *p2p.Server) error {// Start the bloom bits servicing goroutiness.startBloomHandlers(params.BloomBitsBlocks)// Start the RPC services.netRPCService = ethapi.NewPublicNetAPI(srvr, s.NetVersion())// Figure out a max peers count based on the server limitsmaxPeers := srvr.MaxPeersif s.config.LightServ > 0 {if s.config.LightPeers >= srvr.MaxPeers {return fmt.Errorf("invalid peer config: light peer count (%d) >= total peer count (%d)", s.config.LightPeers, srvr.MaxPeers)}maxPeers -= s.config.LightPeers}// Start the networking layer and the light server if requesteds.protocolManager.Start(maxPeers)if s.lesServer != nil {s.lesServer.Start(srvr)}return nil
}

主要的一行代码是s.protocolManager.Start(maxPeers),启动协议管理实例,代码:


func (pm *ProtocolManager) Start(maxPeers int) {pm.maxPeers = maxPeers// broadcast transactionspm.txsCh = make(chan core.NewTxsEvent, txChanSize)pm.txsSub = pm.txpool.SubscribeNewTxsEvent(pm.txsCh)go pm.txBroadcastLoop() //协程开启交易广播// broadcast mined blockspm.minedBlockSub = pm.eventMux.Subscribe(core.NewMinedBlockEvent{})go pm.minedBroadcastLoop() //协程开启挖矿// start sync handlersgo pm.syncer() //协程开启同步go pm.txsyncLoop()//协程开启交易同步
}

上面的广播及同步操作,都采用CSP思想设计,把需要处理的消息放到channel中。然后由p.broadcast()方法处理

现在回到makeFullNode,刚才说过了注册ethereum服务,除此之外还注册了whisper服务,统计服务,最后返回这个node实例。

接下来就进行startNode(ctx, node),主要代码如下:

func startNode(ctx *cli.Context, stack *node.Node) {// Start up the node itselfutils.StartNode(stack) //启动本节点的注册的服务及rpc等,代码下面会分析// Unlock any account specifically requestedks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)//......解锁钱包代码略// Start auxiliary services if enabledif ctx.GlobalBool(utils.MiningEnabledFlag.Name) || ctx.GlobalBool(utils.DeveloperFlag.Name) {//...代码略,获取正在运行的ethereum服务,并设置交易池的gaspriceethereum.TxPool().SetGasPrice(gasprice)//设置挖矿线程数if err := ethereum.StartMining(threads); err != nil { //开启挖矿utils.Fatalf("Failed to start mining: %v", err)}}
}
StartNode代码如下:

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)<-sigclog.Info("Got interrupt, shutting down...")go stack.Stop()for i := 10; i > 0; i-- {<-sigcif 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")}()
}

stack.Start()启动node,然后监听终止信号。

这个node.start方法的代码挺多,但是逻辑主要有两个,如下:

func (n *Node) Start() error {n.lock.Lock()defer n.lock.Unlock()//设置serverconfigrunning := &p2p.Server{Config: n.serverConfig} //实例化一个p2p的server端n.log.Info("Starting peer-to-peer node", "instance", n.serverConfig.Name)//找出本节点支持的service,同时初始化running.Protocolsif err := running.Start(); err != nil { //启动p2p服务端return convertFileLockError(err)}// Start each of the services 然后循环所有支持的服务进行启动service.Start(running)started := []reflect.Type{}for kind, service := range services {// Start the next service, stopping all previous upon failureif err := service.Start(running); err != nil { //启动 ethereum服务就在这启动的for _, kind := range started {services[kind].Stop()}running.Stop()return err}// Mark the service started for potential cleanupstarted = append(started, kind)}// Lastly start the configured RPC interfacesif err := n.startRPC(services); err != nil { //启动rpc服务,包括ipc/http/websocketfor _, service := range services {service.Stop()}running.Stop()return err}// Finish initializing the startupn.services = servicesn.server = runningn.stop = make(chan struct{})return nil
}

下面看p2p服务端的启动代码

func (srv *Server) Start() (err error) {// src参数初始化代码略if err := srv.setupLocalNode(); err != nil {return err}if srv.ListenAddr != "" {if err := srv.setupListening(); err != nil { //启动端口监听return err}}if err := srv.setupDiscovery(); err != nil { //启动节点发现return err}dynPeers := srv.maxDialedConns()dialer := newDialState(srv.localnode.ID(), srv.StaticNodes, srv.BootstrapNodes, srv.ntab, dynPeers, srv.NetRestrict) //获得初始连接的节点srv.loopWG.Add(1)go srv.run(dialer) //启动serverreturn nil
}
go srv.run(dialer)这行代码是关键,负责与初始的节点进行握手并启动节点的连接,代码如下:

func (srv *Server) run(dialstate dialer) {srv.log.Info("Started P2P networking", "self", srv.localnode.Node())defer srv.loopWG.Done()defer srv.nodedb.Close()var (peers        = make(map[enode.ID]*Peer)inboundCount = 0trusted      = make(map[enode.ID]bool, len(srv.TrustedNodes))taskdone     = make(chan task, maxActiveDialTasks)runningTasks []taskqueuedTasks  []task // tasks that can't run yet)// Put trusted nodes into a map to speed up checks.// Trusted peers are loaded on startup or added via AddTrustedPeer RPC.for _, n := range srv.TrustedNodes {trusted[n.ID()] = true}// removes t from runningTasksdelTask := func(t task) {for i := range runningTasks {if runningTasks[i] == t {runningTasks = append(runningTasks[:i], runningTasks[i+1:]...)break}}}// starts until max number of active tasks is satisfiedstartTasks := func(ts []task) (rest []task) {i := 0for ; len(runningTasks) < maxActiveDialTasks && i < len(ts); i++ {t := ts[i]srv.log.Trace("New dial task", "task", t)go func() { t.Do(srv); taskdone <- t }()runningTasks = append(runningTasks, t)}return ts[i:]}scheduleTasks := func() {// Start from queue first.queuedTasks = append(queuedTasks[:0], startTasks(queuedTasks)...)// Query dialer for new tasks and start as many as possible now.if len(runningTasks) < maxActiveDialTasks {nt := dialstate.newTasks(len(runningTasks)+len(queuedTasks), peers, time.Now())queuedTasks = append(queuedTasks, startTasks(nt)...)}}running:for {scheduleTasks()select {case <-srv.quit:// The server was stopped. Run the cleanup logic.break runningcase n := <-srv.addstatic:// This channel is used by AddPeer to add to the// ephemeral static peer list. Add it to the dialer,// it will keep the node connected.srv.log.Trace("Adding static node", "node", n)dialstate.addStatic(n)case n := <-srv.removestatic:// This channel is used by RemovePeer to send a// disconnect request to a peer and begin the// stop keeping the node connected.srv.log.Trace("Removing static node", "node", n)dialstate.removeStatic(n)if p, ok := peers[n.ID()]; ok {p.Disconnect(DiscRequested)}case n := <-srv.addtrusted:// This channel is used by AddTrustedPeer to add an enode// to the trusted node set.srv.log.Trace("Adding trusted node", "node", n)trusted[n.ID()] = true// Mark any already-connected peer as trustedif p, ok := peers[n.ID()]; ok {p.rw.set(trustedConn, true)}case n := <-srv.removetrusted:// This channel is used by RemoveTrustedPeer to remove an enode// from the trusted node set.srv.log.Trace("Removing trusted node", "node", n)if _, ok := trusted[n.ID()]; ok {delete(trusted, n.ID())}// Unmark any already-connected peer as trustedif p, ok := peers[n.ID()]; ok {p.rw.set(trustedConn, false)}case op := <-srv.peerOp:// This channel is used by Peers and PeerCount.op(peers)srv.peerOpDone <- struct{}{}case t := <-taskdone:// A task got done. Tell dialstate about it so it// can update its state and remove it from the active// tasks list.srv.log.Trace("Dial task done", "task", t)dialstate.taskDone(t, time.Now())delTask(t)case c := <-srv.posthandshake:// A connection has passed the encryption handshake so// the remote identity is known (but hasn't been verified yet).if trusted[c.node.ID()] {// Ensure that the trusted flag is set before checking against MaxPeers.c.flags |= trustedConn}// TODO: track in-progress inbound node IDs (pre-Peer) to avoid dialing them.select {case c.cont <- srv.encHandshakeChecks(peers, inboundCount, c):case <-srv.quit:break running}case c := <-srv.addpeer:// At this point the connection is past the protocol handshake.// Its capabilities are known and the remote identity is verified.err := srv.protoHandshakeChecks(peers, inboundCount, c)if err == nil {// The handshakes are done and it passed all checks.p := newPeer(c, srv.Protocols)// If message events are enabled, pass the peerFeed// to the peerif srv.EnableMsgEvents {p.events = &srv.peerFeed}name := truncateName(c.name)srv.log.Debug("Adding p2p peer", "name", name, "addr", c.fd.RemoteAddr(), "peers", len(peers)+1)go srv.runPeer(p)peers[c.node.ID()] = pif p.Inbound() {inboundCount++}}// The dialer logic relies on the assumption that// dial tasks complete after the peer has been added or// discarded. Unblock the task last.select {case c.cont <- err:case <-srv.quit:break running}case pd := <-srv.delpeer:// A peer disconnected.d := common.PrettyDuration(mclock.Now() - pd.created)pd.log.Debug("Removing p2p peer", "duration", d, "peers", len(peers)-1, "req", pd.requested, "err", pd.err)delete(peers, pd.ID())if pd.Inbound() {inboundCount--}}}srv.log.Trace("P2P networking is spinning down")// Terminate discovery. If there is a running lookup it will terminate soon.if srv.ntab != nil {srv.ntab.Close()}if srv.DiscV5 != nil {srv.DiscV5.Close()}// Disconnect all peers.for _, p := range peers {p.Disconnect(DiscQuitting)}// Wait for peers to shut down. Pending connections and tasks are// not handled here and will terminate soon-ish because srv.quit// is closed.for len(peers) > 0 {p := <-srv.delpeerp.log.Trace("<-delpeer (spindown)", "remainingTasks", len(runningTasks))delete(peers, p.ID())}
}

其中case c := <-srv.addpeer 里有go srv.runPeer(p)即与p节点建立连接,主要代码如下:


func (p *Peer) run() (remoteRequested bool, err error) {var (writeStart = make(chan struct{}, 1)writeErr   = make(chan error, 1)readErr    = make(chan error, 1)reason     DiscReason // sent to the peer)p.wg.Add(2)go p.readLoop(readErr) //负责读取消息并处理消息go p.pingLoop() //定时发送Ping// Start all protocol handlers.writeStart <- struct{}{}p.startProtocols(writeStart, writeErr) //启动服务支持的协议// Wait for an error or disconnect.
loop:for {//代码略}close(p.closed)p.rw.close(reason)p.wg.Wait()return remoteRequested, err
}

readLoop是读取连接发送来的数据的,代码


func (p *Peer) readLoop(errc chan<- error) {defer p.wg.Done()for {msg, err := p.rw.ReadMsg()if err != nil {errc <- errreturn}msg.ReceivedAt = time.Now()if err = p.handle(msg); err != nil {errc <- errreturn}}
}

读取消息后,进行处理,代码:

func (p *Peer) handle(msg Msg) error {switch {case msg.Code == pingMsg:msg.Discard()go SendItems(p.rw, pongMsg)case msg.Code == discMsg:var reason [1]DiscReason// This is the last message. We don't need to discard or// check errors because, the connection will be closed after it.rlp.Decode(msg.Payload, &reason)return reason[0]case msg.Code < baseProtocolLength:// ignore other base protocol messagesreturn msg.Discard()default:// it's a subprotocol messageproto, err := p.getProto(msg.Code)if err != nil {return fmt.Errorf("msg code out of range: %v", msg.Code)}select {case proto.in <- msg:return nilcase <-p.closed:return io.EOF}}return nil
}

至此,p2p连接已经建立!节点之间同步区块头、区块体,及交易池的交易等等。

接下来开始挖矿ethereum.StartMining(threads),这个方法主要是设置gasprice,设置etherbase(coinbase)账号,然后执行

go s.miner.Start(eb),代码如下:

func (self *Miner) Start(coinbase common.Address) {atomic.StoreInt32(&self.shouldStart, 1)self.SetEtherbase(coinbase)if atomic.LoadInt32(&self.canStart) == 0 {log.Info("Network syncing, will start miner afterwards")return}self.worker.start()
}
self.worker.start()代码:
func (w *worker) start() {atomic.StoreInt32(&w.running, 1)w.startCh <- struct{}{}
}

设置了w.startCh,一个work的挖矿开关。

这个work是在ethereum.new服务创建时,创建mine时一块创建的,代码:

func New(eth Backend, config *params.ChainConfig, mux *event.TypeMux, engine consensus.Engine, recommit time.Duration, gasFloor, gasCeil uint64, isLocalBlock func(block *types.Block) bool) *Miner {miner := &Miner{eth:      eth,mux:      mux,engine:   engine,exitCh:   make(chan struct{}),worker:   newWorker(config, engine, eth, mux, recommit, gasFloor, gasCeil, isLocalBlock),canStart: 1,}go miner.update()return miner
}

有一个newWorker(config, engine, eth, mux, recommit, gasFloor, gasCeil, isLocalBlock),主要代码:


func newWorker(config *params.ChainConfig, engine consensus.Engine, eth Backend, mux *event.TypeMux, recommit time.Duration, gasFloor, gasCeil uint64, isLocalBlock
//代码略...go worker.mainLoop()go worker.newWorkLoop(recommit)go worker.resultLoop()go worker.taskLoop()// Submit first work to initialize pending state.worker.startCh <- struct{}{}return worker
}

OK吧,挖矿的逻辑还是挺多的,具体怎么挖矿改日再续~

看源码,以太坊的代码量挺多的,模块也划分的挺清晰,只是某些方法注释没有写,项目模块化还可以做的更加松散些,依赖的一些第三方依赖可以专门处理下~

以太坊Go-ethereum源码分析之启动流程相关推荐

  1. Nginx源码分析:启动流程

    nginx源码分析 nginx-1.11.1 参考书籍<深入理解nginx模块开发与架构解析> nginx简介 Nginx的作为服务端软件,表现的主要特点是更快.高扩展.高可靠性.低内存消 ...

  2. 【区块链 | 智能合约】Ethereum源代码(11)- 以太坊核心BlockChain源码分析

    前面几节都在分析以太坊的通信协议,怎么广播,怎么同步,怎么下载.这一节讲讲以太坊的核心模块BlockChain,也就是以太坊的区块链. 一,BlockChain的初始化 Ethereum服务初始化fu ...

  3. 以太坊Ethash算法源码分析

    Ethash是以太坊目前使用的共识算法,其前身是Dagger-Hashimoto算法,但是进行了很大的改动. 1. Dagger-Hashimoto Dagger-Hashimoto算法想要达到以下几 ...

  4. 以太坊共识引擎源码分析

    这一篇分析以太坊的共识引擎,先看一下各组件之间的关系: Engine接口定义了共识引擎需要实现的所有函数,实际上按功能可以划分为2类: 区块验证类:以Verify开头,当收到新区块时,需要先验证区块的 ...

  5. open-ethereum-pool以太坊矿池源码分析(1)环境安装

    # open-ethereum-pool以太坊矿池-环境安装 ## 安装Geth ```shell //安装parity cd /tmp/ wget http://d1h4xl4cr1h0mo.clo ...

  6. open-ethereum-pool以太坊矿池源码分析(5)proxy模块

    # open-ethereum-pool以太坊矿池-proxy模块 ## ProxyServer定义 ```go type ProxyServer struct { config *Config bl ...

  7. Kubelet源码分析(一):启动流程分析

    源码版本 kubernetes version: v1.3.0 简介 在Kubernetes急群众,在每个Node节点上都会启动一个kubelet服务进程.该进程用于处理Master节点下发到本节点的 ...

  8. Android进阶——Small源码分析之启动流程详解

    前言 插件化现在已经是Android工程师必备的技能之一,只是学会怎么使用是不行的,所以蹭有时间研究一下Small的源码.对于插件化主要解决的问题是四大组件的加载和资源的加载,读懂所有Small源码需 ...

  9. Flask1.1.4 Werkzeug1.0.1 源码分析:启动流程

    基于QuickStart中的一个demo来分析 from flask import Flaskapp = Flask(__name__)@app.route("/") def he ...

最新文章

  1. 【转】eclipse android 设置及修改生成apk的签名文件 -- custom debug keystore
  2. 怎么做好企业IT运维工作
  3. k8s安装kubesphere的环境准备:资源规划、默认存储类StorageClass(nfs-client-provisioner)
  4. python 分布式队列_〖Python〗-- Celery分布式任务队列
  5. VS Code 调试 PHP有关配置
  6. java中的equals拿什么鞋的_java.中equals的使用
  7. Direct3D播放RGB(通过Texture)
  8. ORACLE START WITH 语句的树级结构例子
  9. 定时关机 v1.0(autoshut v1.0)
  10. c# webclient 保存会话信息_winform项目——仿QQ即时通讯程序16:会话列表的存储
  11. jquery悬停_jQuery悬停()
  12. Cesium:向地图中添加线的方法
  13. python 回调函数的使用_Python回调函数用法实例详解
  14. drony+fiddler抓包
  15. marked is not a function问题解决
  16. 深度卷积神经网络之AlexNet
  17. ffmpeg如何实现MP3转码g711a
  18. java编程学习入门
  19. 对Simulink模型进行保护的方法
  20. 参与IPFS项目最好的方式是买矿机挖Fil,而不是直接买币

热门文章

  1. 从CSDN账户密码被盗说起
  2. 银联在线支付5100030异常
  3. 安利7款高质量的Mac软件,每一款都让人相见恨晚!
  4. matlab中for循环的简单使用
  5. 蓝牙设备通过手机空中升级固件步骤
  6. nbu15_克鲁勃NBU 15
  7. 浅谈 Java 并发下的乐观锁
  8. java扫描接口_一种扫描接口并生成可调用API接口文档的方法与流程
  9. 支付宝开放平台当面付接口整体解析
  10. 搭建gloo网关(基于envoy)的wasm实验环境(阿里云、本机)