比特币全节点Go语言实现BTCD之启动流程

~~~

直接执行btcd启动:

[root@bitcoin ~]# btcd
2018-07-01 15:07:34.645 [INF] BTCD: Version 0.12.0-beta
2018-07-01 15:07:34.645 [INF] BTCD: Loading block database from '/root/.btcd/data/mainnet/blocks_ffldb'
2018-07-01 15:07:34.655 [TRC] BCDB: Scan found latest block file #10 with length 479729657
2018-07-01 15:07:34.655 [INF] BTCD: Block database loaded
2018-07-01 15:07:34.661 [INF] INDX: cf index is enabled
2018-07-01 15:07:34.662 [TRC] CHAN: Serialized chain state: d97b2220ae8cbc1c881eed2faa7062205bd3d91ed0720a4831040000000000001e6503007131c90000000000090000002b80942403a92fc008
2018-07-01 15:07:34.662 [INF] CHAN: Loading block index...
2018-07-01 15:07:37.063 [DBG] INDX: Current committed filter index tip (height 222494, hash 0000000000000431480a72d01ed9d35b206270aa2fed1e881cbc8cae20227bd9)
2018-07-01 15:07:37.373 [INF] CHAN: Chain state (height 222494, hash 0000000000000431480a72d01ed9d35b206270aa2fed1e881cbc8cae20227bd9, totaltx 13185393, work 802475065101060653064)
2018-07-01 15:07:37.417 [TRC] SRVR: Starting server
2018-07-01 15:07:37.417 [TRC] RPCS: Starting RPC server
2018-07-01 15:07:37.417 [TRC] AMGR: Starting address manager
2018-07-01 15:07:37.437 [INF] RPCS: RPC server listening on [::1]:8334
2018-07-01 15:07:37.437 [INF] RPCS: RPC server listening on 127.0.0.1:8334
2018-07-01 15:07:37.500 [INF] AMGR: Loaded 4849 addresses from file '/root/.btcd/data/mainnet/peers.json'
2018-07-01 15:07:37.500 [TRC] SYNC: Starting sync manager
2018-07-01 15:07:37.500 [TRC] SRVR: Starting peer handler
2018-07-01 15:07:37.500 [TRC] CMGR: Connection manager started
2018-07-01 15:07:37.507 [TRC] AMGR: Selected 112.82.111.33:8333 from new bucket
2018-07-01 15:07:37.507 [DBG] CMGR: Attempting to connect to 112.82.111.33:8333 (reqid 1)
2018-07-01 15:07:37.508 [INF] CMGR: Server listening on 0.0.0.0:8333
2018-07-01 15:07:37.508 [INF] CMGR: Server listening on [::]:8333
2018-07-01 15:07:37.508 [TRC] AMGR: Selected 85.195.215.138:8333 from new bucket
2018-07-01 15:07:37.508 [DBG] CMGR: Attempting to connect to 85.195.215.138:8333 (reqid 2)
2018-07-01 15:07:37.508 [TRC] AMGR: Selected 197.89.233.83:8333 from tried bucket
2018-07-01 15:07:37.508 [DBG] CMGR: Attempting to connect to 197.89.233.83:8333 (reqid 3)
2018-07-01 15:07:37.508 [TRC] AMGR: Selected [2600:1f18:609f:da01:1c94:673e:4141:423f]:8333 from new bucket
2018-07-01 15:07:37.508 [DBG] CMGR: Attempting to connect to [2600:1f18:609f:da01:1c94:673e:4141:423f]:8333 (reqid 4)
2018-07-01 15:07:37.508 [DBG] CMGR: Failed to connect to [2600:1f18:609f:da01:1c94:673e:4141:423f]:8333 (reqid 4): dial tcp [2600:1f18:609f:da01:1c94:673e:4141:423f]:8333: connect: network is unreachable
2018-07-01 15:07:37.508 [TRC] AMGR: Selected 88.99.185.48:8333 from tried bucket
2018-07-01 15:07:37.508 [DBG] CMGR: Attempting to connect to 88.99.185.48:8333 (reqid 5)
2018-07-01 15:07:37.508 [TRC] AMGR: Selected 197.89.233.83:8333 from tried bucket
2018-07-01 15:07:37.508 [DBG] CMGR: Attempting to connect to 197.89.233.83:8333 (reqid 6)
2018-07-01 15:07:37.508 [TRC] AMGR: Selected [2002:8e00:8231::8e00:8231]:8333 from new bucket
2018-07-01 15:07:37.508 [DBG] CMGR: Attempting to connect to [2002:8e00:8231::8e00:8231]:8333 (reqid 7)
2018-07-01 15:07:37.508 [DBG] CMGR: Failed to connect to [2002:8e00:8231::8e00:8231]:8333 (reqid 7): dial tcp [2002:8e00:8231::8e00:8231]:8333: connect: network is unreachable
2018-07-01 15:07:37.508 [TRC] AMGR: Selected 185.9.63.169:8333 from new bucket
2018-07-01 15:07:37.508 [DBG] CMGR: Attempting to connect to 185.9.63.169:8333 (reqid 8)
2018-07-01 15:07:37.508 [TRC] AMGR: Selected 142.169.246.147:8333 from new bucket
2018-07-01 15:07:37.508 [DBG] CMGR: Attempting to connect to 142.169.246.147:8333 (reqid 9)
2018-07-01 15:07:37.508 [TRC] AMGR: Selected 216.157.21.35:8333 from tried bucket
2018-07-01 15:07:37.508 [DBG] CMGR: Attempting to connect to 216.157.21.35:8333 (reqid 10)
2018-07-01 15:07:37.563 [INF] CMGR: 38 addresses found from DNS seed seed.bitcoin.jonasschnelli.ch
2018-07-01 15:07:37.563 [TRC] AMGR: Added new address 88.99.187.182:8333 for a total of 4849 addresses
2018-07-01 15:07:37.563 [TRC] AMGR: Added new address 101.37.116.129:8333 for a total of 4849 addresses
2018-07-01 15:07:37.563 [TRC] AMGR: Added new address [2001:0:4137:9e76:47f:240a:93e3:3222]:8333 for a total of 4850 addresses
2018-07-01 15:07:37.563 [TRC] AMGR: Added new address [2001:0:4137:9e76:4a5:332b:88a1:a284]:8333 for a total of 4851 addresses
2018-07-01 15:07:37.563 [TRC] AMGR: Added new address [2001:0:4137:9e76:10c6:3f05:a79f:85d1]:8333 for a total of 4852 addresses
2018-07-01 15:07:37.563 [TRC] AMGR: Added new address [2001:0:4137:9e76:145f:32c3:2ee1:6a8c]:8333 for a total of 4852 addresses
2018-07-01 15:07:37.563 [TRC] AMGR: Added new address [2001:0:4137:9e76:24a5:375:a9fa:a96e]:8333 for a total of 4853 addresses

下面开始分析源码~

执行入口:

func main() {
   // Use all processor cores.
   runtime.GOMAXPROCS(runtime.NumCPU())

   // Block and transaction processing can cause bursty allocations.  This
   // limits the garbage collector from excessively overallocating during
   // bursts.  This value was arrived at with the help of profiling live
   // usage.
   debug.SetGCPercent(10)

   // Up some limits.
   if err := limits.SetLimits(); err != nil {
      fmt.Fprintf(os.Stderr, "failed to set limits: %v\n", err)
      os.Exit(1)
   }

   if runtime.GOOS == "windows" {
      isService, err := winServiceMain()
      if err != nil {
         fmt.Println(err)
         os.Exit(1)
      }
      if isService {
         os.Exit(0)
      }
   }

   // Work around defer not working after os.Exit()
   if err := btcdMain(nil); err != nil {
      os.Exit(1)
   }
}
runtime.GOMAXPROCS(runtime.NumCPU())

设置使用的CPU核数,默认已经设置所有

debug.SetGCPercent(10)

设置垃圾回收比率

limits.SetLimits()

limits包中有针对不同平台的三个文件:limits_plan9.go limits_unix.go limits_windows.go

设置系统资源限制

btcdMain(nil)
这里才是真正干活的Main方法

btcMain方法主要逻辑:

package main

import (
   "net"
   "net/http"
   "os"
   "runtime/pprof"
)

func btcdMain(serverChan chan<- *server) error {
   tcfg, _, err := loadConfig()
   interrupt := interruptListener()
   // Enable http profiling server if requested.
   if cfg.Profile != "" {
      go func() {
         listenAddr := net.JoinHostPort("", cfg.Profile)
         btcdLog.Infof("Profile server listening on %s", listenAddr)
         profileRedirect := http.RedirectHandler("/debug/pprof", http.StatusSeeOther)
         http.Handle("/", profileRedirect)
         btcdLog.Errorf("%v", http.ListenAndServe(listenAddr, nil))
      }()
   }
   // Write cpu profile if requested.
   if cfg.CPUProfile != "" {
      f, err := os.Create(cfg.CPUProfile)
      if err != nil {
         btcdLog.Errorf("Unable to create cpu profile: %v", err)
         return err
      }
      pprof.StartCPUProfile(f)
      defer f.Close()
      defer pprof.StopCPUProfile()
   }
   // Return now if an interrupt signal was triggered.
   if interruptRequested(interrupt) {
      return nil
   }
   // Load the block database.
   db, err := loadBlockDB()
   defer func() {
      // Ensure the database is sync'd and closed on shutdown.
      btcdLog.Infof("Gracefully shutting down the database...")
      db.Close()
   }()

   // Create server and start it.
   server, err := newServer(cfg.Listeners, db, activeNetParams.Params, interrupt)
   defer func() {
      btcdLog.Infof("Gracefully shutting down the server...")
      server.Stop()
      server.WaitForShutdown()
      srvrLog.Infof("Server shutdown complete")
   }()
   server.Start()

   <-interrupt
   return nil
}

loadConfig()

加载配置信息,使用了github.com/jessevdk/go-flags这个第三方库,可以实现从配置文件或命令行获取参数值,功能挺强大的库,近期也有一直在维护。

func interruptListener() <-chan struct{} {

这个方法主要是使用signal.Notify(interruptChannel, interruptSignals...)来监听系统interrupt的signal,如有中断则close channel。

db, err := loadBlockDB()

根据cfg.DbType配置打开数据库连接。通过代码得知支持数据库有dbTypes := []string{"ffldb", "leveldb", "sqlite"}

server, err := newServer(cfg.Listeners, db, activeNetParams.Params, interrupt)

使用配置的端口、刚连接的db及网络参数初始一个server对象。

activeNetParams.Params是硬编码参数,如下:

var MainNetParams = Params{
   Name:        "mainnet",
   Net:         wire.MainNet,
   DefaultPort: "8333",
   DNSSeeds: []DNSSeed{
      {"seed.bitcoin.sipa.be", true},
      {"dnsseed.bluematt.me", true},
      {"dnsseed.bitcoin.dashjr.org", false},
      {"seed.bitcoinstats.com", true},
      {"seed.bitnodes.io", false},
      {"seed.bitcoin.jonasschnelli.ch", true},
   },

   // Chain parameters
   GenesisBlock:             &genesisBlock,

硬编码了主网名称、默认端口、起始dns种子等。

接下来咱们详细分析newServer方法。主要处理逻辑有:

func newServer(listenAddrs []string, db database.DB, chainParams *chaincfg.Params, interrupt <-chan struct{}) (*server, error) {
   defaultServices = wire.SFNodeNetwork | wire.SFNodeBloom | wire.SFNodeWitness | wire.SFNodeCF
   services := defaultServices
   amgr := addrmgr.New(cfg.DataDir, btcdLookup)

   listeners, nat, err := initListeners(amgr, listenAddrs, services)

   s := server{
      chainParams:          chainParams,
      addrManager:          amgr,
      newPeers:             make(chan *serverPeer, cfg.MaxPeers),
      donePeers:            make(chan *serverPeer, cfg.MaxPeers),
      banPeers:             make(chan *serverPeer, cfg.MaxPeers),
      query:                make(chan interface{}),
      relayInv:             make(chan relayMsg, cfg.MaxPeers),
      broadcast:            make(chan broadcastMsg, cfg.MaxPeers),
      quit:                 make(chan struct{}),
      modifyRebroadcastInv: make(chan interface{}),
      peerHeightsUpdate:    make(chan updatePeerHeightsMsg),
      nat:                  nat,
      db:                   db,
      timeSource:           blockchain.NewMedianTime(),
      services:             services,
      sigCache:             txscript.NewSigCache(cfg.SigCacheMaxSize),
      hashCache:            txscript.NewHashCache(cfg.SigCacheMaxSize),
      cfCheckptCaches:      make(map[wire.FilterType][]cfHeaderKV),
   }

   s.txIndex = indexers.NewTxIndex(db)
   indexes = append(indexes, s.addrIndex)
   indexes = append(indexes, s.cfIndex)
   indexManager = indexers.NewManager(db, indexes)

   // Merge given checkpoints with the default ones unless they are disabled.
   var checkpoints []chaincfg.Checkpoint
   if !cfg.DisableCheckpoints {
      checkpoints = mergeCheckpoints(s.chainParams.Checkpoints, cfg.addCheckpoints)
   }

   // Create a new block chain instance with the appropriate configuration.
   var err error
   s.chain, err = blockchain.New(&blockchain.Config{
      DB:           s.db,
      Interrupt:    interrupt,
      ChainParams:  s.chainParams,
      Checkpoints:  checkpoints,
      TimeSource:   s.timeSource,
      SigCache:     s.sigCache,
      IndexManager: indexManager,
      HashCache:    s.hashCache,
   })
   if err != nil {
      return nil, err
   }

   // Search for a FeeEstimator state in the database. If none can be found
   // or if it cannot be loaded, create a new one.
   db.Update(func(tx database.Tx) error {
      metadata := tx.Metadata()
      feeEstimationData := metadata.Get(mempool.EstimateFeeDatabaseKey)
      if feeEstimationData != nil {
         // delete it from the database so that we don't try to restore the
         // same thing again somehow.
         metadata.Delete(mempool.EstimateFeeDatabaseKey)

         // If there is an error, log it and make a new fee estimator.
         var err error
         s.feeEstimator, err = mempool.RestoreFeeEstimator(feeEstimationData)

         if err != nil {
            peerLog.Errorf("Failed to restore fee estimator %v", err)
         }
      }

      return nil
   })

   if s.feeEstimator == nil || s.feeEstimator.LastKnownHeight() != s.chain.BestSnapshot().Height {
      s.feeEstimator = mempool.NewFeeEstimator(
         mempool.DefaultEstimateFeeMaxRollback,
         mempool.DefaultEstimateFeeMinRegisteredBlocks)
   }

   txC := mempool.Config{
      Policy: mempool.Policy{
         DisableRelayPriority: cfg.NoRelayPriority,
         AcceptNonStd:         cfg.RelayNonStd,
         FreeTxRelayLimit:     cfg.FreeTxRelayLimit,
         MaxOrphanTxs:         cfg.MaxOrphanTxs,
         MaxOrphanTxSize:      defaultMaxOrphanTxSize,
         MaxSigOpCostPerTx:    blockchain.MaxBlockSigOpsCost / 4,
         MinRelayTxFee:        cfg.minRelayTxFee,
         MaxTxVersion:         2,
      },
      ChainParams:    chainParams,
      FetchUtxoView:  s.chain.FetchUtxoView,
      BestHeight:     func() int32 { return s.chain.BestSnapshot().Height },
      MedianTimePast: func() time.Time { return s.chain.BestSnapshot().MedianTime },
      CalcSequenceLock: func(tx *btcutil.Tx, view *blockchain.UtxoViewpoint) (*blockchain.SequenceLock, error) {
         return s.chain.CalcSequenceLock(tx, view, true)
      },
      IsDeploymentActive: s.chain.IsDeploymentActive,
      SigCache:           s.sigCache,
      HashCache:          s.hashCache,
      AddrIndex:          s.addrIndex,
      FeeEstimator:       s.feeEstimator,
   }
   s.txMemPool = mempool.New(&txC)

   s.syncManager, err = netsync.New(&netsync.Config{
      PeerNotifier:       &s,
      Chain:              s.chain,
      TxMemPool:          s.txMemPool,
      ChainParams:        s.chainParams,
      DisableCheckpoints: cfg.DisableCheckpoints,
      MaxPeers:           cfg.MaxPeers,
      FeeEstimator:       s.feeEstimator,
   })
   if err != nil {
      return nil, err
   }

   policy := mining.Policy{
      BlockMinWeight:    cfg.BlockMinWeight,
      BlockMaxWeight:    cfg.BlockMaxWeight,
      BlockMinSize:      cfg.BlockMinSize,
      BlockMaxSize:      cfg.BlockMaxSize,
      BlockPrioritySize: cfg.BlockPrioritySize,
      TxMinFreeFee:      cfg.minRelayTxFee,
   }
   blockTemplateGenerator := mining.NewBlkTmplGenerator(&policy,
      s.chainParams, s.txMemPool, s.chain, s.timeSource,
      s.sigCache, s.hashCache)
   s.cpuMiner = cpuminer.New(&cpuminer.Config{
      ChainParams:            chainParams,
      BlockTemplateGenerator: blockTemplateGenerator,
      MiningAddrs:            cfg.miningAddrs,
      ProcessBlock:           s.syncManager.ProcessBlock,
      ConnectedCount:         s.ConnectedCount,
      IsCurrent:              s.syncManager.IsCurrent,
   })
   
   var newAddressFunc func() (net.Addr, error)

   // Create a connection manager.
   targetOutbound := defaultTargetOutbound
   if cfg.MaxPeers < targetOutbound {
      targetOutbound = cfg.MaxPeers
   }
   cmgr, err := connmgr.New(&connmgr.Config{
      Listeners:      listeners,
      OnAccept:       s.inboundPeerConnected,
      RetryDuration:  connectionRetryInterval,
      TargetOutbound: uint32(targetOutbound),
      Dial:           btcdDial,
      OnConnection:   s.outboundPeerConnected,
      GetNewAddress:  newAddressFunc,
   })
   if err != nil {
      return nil, err
   }
   s.connManager = cmgr

   // Start up persistent peers.
   permanentPeers := cfg.ConnectPeers
   if len(permanentPeers) == 0 {
      permanentPeers = cfg.AddPeers
   }
   for _, addr := range permanentPeers {
      netAddr, err := addrStringToNetAddr(addr)
      if err != nil {
         return nil, err
      }

      go s.connManager.Connect(&connmgr.ConnReq{
         Addr:      netAddr,
         Permanent: true,
      })
   }

   if !cfg.DisableRPC {
      // Setup listeners for the configured RPC listen addresses and
      // TLS settings.
      rpcListeners, err := setupRPCListeners()
      if err != nil {
         return nil, err
      }
      if len(rpcListeners) == 0 {
         return nil, errors.New("RPCS: No valid listen address")
      }

      s.rpcServer, err = newRPCServer(&rpcserverConfig{
         Listeners:    rpcListeners,
         StartupTime:  s.startupTime,
         ConnMgr:      &rpcConnManager{&s},
         SyncMgr:      &rpcSyncMgr{&s, s.syncManager},
         TimeSource:   s.timeSource,
         Chain:        s.chain,
         ChainParams:  chainParams,
         DB:           db,
         TxMemPool:    s.txMemPool,
         Generator:    blockTemplateGenerator,
         CPUMiner:     s.cpuMiner,
         TxIndex:      s.txIndex,
         AddrIndex:    s.addrIndex,
         CfIndex:      s.cfIndex,
         FeeEstimator: s.feeEstimator,
      })

   }

   return &s, nil
}

defaultServices = wire.SFNodeNetwork | wire.SFNodeBloom | wire.SFNodeWitness | wire.SFNodeCF

btcd默认启用全节点、Bloom过滤器、见证隔离、提交过滤。

amgr := addrmgr.New(cfg.DataDir, btcdLookup)
创建一个网络地址管理对象,负责管理与其连接的地址(支持tor地址)。所有连接的节点数据都默认保存到peers.json文件中。


listeners, nat, err := initListeners(amgr, listenAddrs, services)

使用配置的externalIPs或upnp对外部IP监听

s := server{

创建server对象,其中有对chainParams、sigCache、hashCache等的初始化

indexManager = indexers.NewManager(db, indexes)

使用txIndex、addrIndex、cfIndex创建一个索引管理器

s.chain, err = blockchain.New(&blockchain.Config{
创建一个blockchain对象并赋值给server对象

s.txMemPool = mempool.New(&txC)
创建未打包的交易内存池

s.syncManager, err = netsync.New(&netsync.Config{

新建网络同步对象,并赋值给server.syncManager

policy := mining.Policy{

新建挖矿策略

s.cpuMiner = cpuminer.New(&cpuminer.Config{

创建cpu挖矿对象

cmgr, err := connmgr.New(&connmgr.Config{

创建节点连接对象

s.rpcServer, err = newRPCServer(&rpcserverConfig{
创建RPCserver

return &s, nil
最后返回创建的server对象

接下来就可以执行:

server.Start()

启动代码:

// Start begins accepting connections from peers.
func (s *server) Start() {
   // Already started?
   if atomic.AddInt32(&s.started, 1) != 1 {
      return
   }

   srvrLog.Trace("Starting server")

   // Server startup time. Used for the uptime command for uptime calculation.
   s.startupTime = time.Now().Unix()

   // Start the peer handler which in turn starts the address and block
   // managers.
   s.wg.Add(1)
   go s.peerHandler()

   if s.nat != nil {
      s.wg.Add(1)
      go s.upnpUpdateThread()
   }

   if !cfg.DisableRPC {
      s.wg.Add(1)

      // Start the rebroadcastHandler, which ensures user tx received by
      // the RPC server are rebroadcast until being included in a block.
      go s.rebroadcastHandler()

      s.rpcServer.Start()
   }

   // Start the CPU miner if generation is enabled.
   if cfg.Generate {
      s.cpuMiner.Start()
   }
}
atomic.AddInt32(&s.started, 1) != 1

设置只启动一次

go s.peerHandler()

异步启动节点处理,包括启动地址管理、启动区块同步、启动连接管理。peerHandler处理逻辑较多,代码在最后解析。

s.rpcServer.Start()

启动rpcserver

s.cpuMiner.Start()
开始CPU挖矿


peerHandler代码主要逻辑:

func (s *server) peerHandler() {
   s.addrManager.Start()
   s.syncManager.Start()

   srvrLog.Tracef("Starting peer handler")

   state := &peerState{
      inboundPeers:    make(map[int32]*serverPeer),
      persistentPeers: make(map[int32]*serverPeer),
      outboundPeers:   make(map[int32]*serverPeer),
      banned:          make(map[string]time.Time),
      outboundGroups:  make(map[string]int),
   }

   if !cfg.DisableDNSSeed {
      // Add peers discovered through DNS to the address manager.
      connmgr.SeedFromDNS(activeNetParams.Params, defaultRequiredServices,
         btcdLookup, func(addrs []*wire.NetAddress) {
            // Bitcoind uses a lookup of the dns seeder here. This
            // is rather strange since the values looked up by the
            // DNS seed lookups will vary quite a lot.
            // to replicate this behaviour we put all addresses as
            // having come from the first one.
            s.addrManager.AddAddresses(addrs, addrs[0])
         })
   }
   go s.connManager.Start()

out:
   for {
      select {
      // New peers connected to the server.
      case p := <-s.newPeers:
         s.handleAddPeerMsg(state, p)

      // Disconnected peers.
      case p := <-s.donePeers:
         s.handleDonePeerMsg(state, p)

      // Block accepted in mainchain or orphan, update peer height.
      case umsg := <-s.peerHeightsUpdate:
         s.handleUpdatePeerHeights(state, umsg)

      // Peer to ban.
      case p := <-s.banPeers:
         s.handleBanPeerMsg(state, p)

      // New inventory to potentially be relayed to other peers.
      case invMsg := <-s.relayInv:
         s.handleRelayInvMsg(state, invMsg)

      // Message to broadcast to all connected peers except those
      // which are excluded by the message.
      case bmsg := <-s.broadcast:
         s.handleBroadcastMsg(state, &bmsg)

      case qmsg := <-s.query:
         s.handleQuery(state, qmsg)

      case <-s.quit:
         // Disconnect all peers on server shutdown.
         state.forAllPeers(func(sp *serverPeer) {
            srvrLog.Tracef("Shutdown peer %s", sp)
            sp.Disconnect()
         })
         break out
      }
   }

   s.connManager.Stop()
   s.syncManager.Stop()
   s.addrManager.Stop()

   // Drain channels before exiting so nothing is left waiting around
   // to send.
cleanup:
   for {
      select {
      case <-s.newPeers:
      case <-s.donePeers:
      case <-s.peerHeightsUpdate:
      case <-s.relayInv:
      case <-s.broadcast:
      case <-s.query:
      default:
         break cleanup
      }
   }
   s.wg.Done()
   srvrLog.Tracef("Peer handler done")
}
s.addrManager.Start()

启动节点地址管理,方法主要逻辑:

func (a *AddrManager) Start() {
   // Load peers we already know about from file.
   a.loadPeers()
}
loadPeers负责反序列化peers.json文件,addrManager维护了NewBucket和TriedBucket两个地址列表

s.syncManager.Start()

主要代码逻辑:

func (sm *SyncManager) Start() {
   go sm.blockHandler()
}

blockHandler作用主要负责处理新节点、交易、块信息、头信息、节点断开等消息。代码如下:

func (sm *SyncManager) blockHandler() {
out:
   for {
      select {
      case m := <-sm.msgChan:
         switch msg := m.(type) {
         case *newPeerMsg:
            sm.handleNewPeerMsg(msg.peer)

         case *txMsg:
            sm.handleTxMsg(msg)
            msg.reply <- struct{}{}

         case *blockMsg:
            sm.handleBlockMsg(msg)
            msg.reply <- struct{}{}

         case *invMsg:
            sm.handleInvMsg(msg)

         case *headersMsg:
            sm.handleHeadersMsg(msg)

         case *donePeerMsg:
            sm.handleDonePeerMsg(msg.peer)

         case getSyncPeerMsg:
            var peerID int32
            if sm.syncPeer != nil {
               peerID = sm.syncPeer.ID()
            }
            msg.reply <- peerID

         case processBlockMsg:
            _, isOrphan, err := sm.chain.ProcessBlock(
               msg.block, msg.flags)
            if err != nil {
               msg.reply <- processBlockResponse{
                  isOrphan: false,
                  err:      err,
               }
            }

            msg.reply <- processBlockResponse{
               isOrphan: isOrphan,
               err:      nil,
            }

         case isCurrentMsg:
            msg.reply <- sm.current()

         case pauseMsg:
            // Wait until the sender unpauses the manager.
            <-msg.unpause

         default:
            log.Warnf("Invalid message type in block "+
               "handler: %T", msg)
         }

      case <-sm.quit:
         break out
      }
   }

   sm.wg.Done()
   log.Trace("Block handler done")
}
go s.connManager.Start()

启动连接管理,默认建立8个出口连接,负责管理连接的连接、断开。主要代码:

func (cm *ConnManager) Start() {
   go cm.connHandler()

   for i := atomic.LoadUint64(&cm.connReqCount); i < uint64(cm.cfg.TargetOutbound); i++ {
      go cm.NewConnReq()
   }
}
go cm.NewConnReq()

是建立节点连接

s.cpuMiner.Start()

启动cpu挖矿

func (m *CPUMiner) Start() {

   go m.speedMonitor()
   go m.miningWorkerController()

   m.started = true
   log.Infof("CPU miner started")
}
go m.miningWorkerController()

这是挖矿操作,启动多个协程进行生成区块。

func (m *CPUMiner) miningWorkerController() {

   var runningWorkers []chan struct{}
   launchWorkers := func(numWorkers uint32) {
      for i := uint32(0); i < numWorkers; i++ {
         quit := make(chan struct{})
         runningWorkers = append(runningWorkers, quit)

         m.workerWg.Add(1)
         go m.generateBlocks(quit)
      }
   }

   // Launch the current number of workers by default.
   runningWorkers = make([]chan struct{}, 0, m.numWorkers)
   launchWorkers(m.numWorkers)
}
go m.generateBlocks(quit)

这一行代码是生成区块,主要逻辑:

func (m *CPUMiner) generateBlocks(quit chan struct{}) {
   log.Tracef("Starting generate blocks worker")

out:
   for {
      m.submitBlockLock.Lock()
      curHeight := m.g.BestSnapshot().Height
      if curHeight != 0 && !m.cfg.IsCurrent() {
         m.submitBlockLock.Unlock()
         time.Sleep(time.Second)
         continue
      }

      // Choose a payment address at random.
      rand.Seed(time.Now().UnixNano())
      payToAddr := m.cfg.MiningAddrs[rand.Intn(len(m.cfg.MiningAddrs))]

      template, err := m.g.NewBlockTemplate(payToAddr)
      m.submitBlockLock.Unlock()

      if m.solveBlock(template.Block, curHeight+1, ticker, quit) {
         block := btcutil.NewBlock(template.Block)
         m.submitBlock(block)
      }
   }

   log.Tracef("Generate blocks worker done")
}
m.solveBlock(template.Block, curHeight+1, ticker, quit)

进行POW计算区块hash,主要逻辑:

func (m *CPUMiner) solveBlock(msgBlock *wire.MsgBlock, blockHeight int32,
   ticker *time.Ticker, quit chan struct{}) bool {

   // Create some convenience variables.
   header := &msgBlock.Header
   targetDifficulty := blockchain.CompactToBig(header.Bits)

   // Initial state.
   lastGenerated := time.Now()
   lastTxUpdate := m.g.TxSource().LastUpdated()
   hashesCompleted := uint64(0)

   for extraNonce := uint64(0); extraNonce < maxExtraNonce; extraNonce++ {
      m.g.UpdateExtraNonce(msgBlock, blockHeight, extraNonce+enOffset)

      for i := uint32(0); i <= maxNonce; i++ {
         select {
         case <-quit:
            return false

         case <-ticker.C:
            m.updateHashes <- hashesCompleted
            hashesCompleted = 0

            best := m.g.BestSnapshot()
            if !header.PrevBlock.IsEqual(&best.Hash) {
               return false
            }

            if lastTxUpdate != m.g.TxSource().LastUpdated() &&
               time.Now().After(lastGenerated.Add(time.Minute)) {

               return false
            }

            m.g.UpdateBlockTime(msgBlock)

         default:
         }

         header.Nonce = i
         hash := header.BlockHash()
         hashesCompleted += 2
         if blockchain.HashToBig(&hash).Cmp(targetDifficulty) <= 0 {
            m.updateHashes <- hashesCompleted
            return true
         }
      }
   }

   return false
}
以上代码即是循环设置Nonce值计算hash值小于难度值



本文作者:architect.bian,欢迎收藏,转载请保留原文地址并保留版权声明!谢谢~
还没完!往下看!!!













猜你喜欢

转载自blog.csdn.net/vohyeah/article/details/80875657